diff --git a/Android.mk b/Android.mk index e31e23f5c2154..0fbb72ebc0d74 100644 --- a/Android.mk +++ b/Android.mk @@ -101,10 +101,12 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothManagerCallback.aidl \ core/java/android/bluetooth/IBluetoothPbap.aidl \ core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \ + core/java/android/bluetooth/IBluetoothHandsfreeClient.aidl \ core/java/android/bluetooth/IBluetoothGatt.aidl \ core/java/android/bluetooth/IBluetoothGattCallback.aidl \ core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \ core/java/android/bluetooth/IBluetoothSap.aidl \ + core/java/android/bluetooth/IBluetoothDun.aidl \ core/java/android/content/IClipboard.aidl \ core/java/android/content/IContentService.aidl \ core/java/android/content/IIntentReceiver.aidl \ @@ -121,8 +123,10 @@ LOCAL_SRC_FILES += \ core/java/android/content/pm/IPackageStatsObserver.aidl \ core/java/android/database/IContentObserver.aidl \ core/java/android/hardware/ISerialManager.aidl \ + core/java/android/hardware/display/IDisplayDevice.aidl \ core/java/android/hardware/display/IDisplayManager.aidl \ core/java/android/hardware/display/IDisplayManagerCallback.aidl \ + core/java/android/hardware/display/IRemoteDisplayAdapter.aidl \ core/java/android/hardware/input/IInputManager.aidl \ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ core/java/android/hardware/location/IGeofenceHardware.aidl \ @@ -154,6 +158,7 @@ LOCAL_SRC_FILES += \ core/java/android/service/notification/INotificationListener.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ + core/java/android/service/gesture/IGestureService.aidl \ core/java/android/service/pie/IPieService.aidl \ core/java/android/service/pie/IPieActivationListener.aidl \ core/java/android/service/pie/IPieHostCallback.aidl \ @@ -236,9 +241,12 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRingtonePlayer.aidl \ telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \ telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ + telephony/java/com/android/internal/telephony/msim/IPhoneSubInfoMSim.aidl \ telephony/java/com/android/internal/telephony/ITelephony.aidl \ + telephony/java/com/android/internal/telephony/msim/ITelephonyMSim.aidl \ telephony/java/com/android/internal/telephony/ISms.aidl \ telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \ + telephony/java/com/android/internal/telephony/ITelephonyRegistryMSim.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ @@ -350,7 +358,9 @@ aidl_files := \ frameworks/base/location/java/com/android/internal/location/ProviderRequest.aidl \ frameworks/base/telephony/java/android/telephony/ServiceState.aidl \ frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ + frameworks/base/telephony/java/com/android/internal/telephony/msim/IPhoneSubInfoMSim.aidl \ frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \ + frameworks/base/telephony/java/com/android/internal/telephony/msim/ITelephonyMSim.aidl \ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl $(gen): PRIVATE_SRC_FILES := $(aidl_files) diff --git a/api/current.txt b/api/current.txt index 71d634668f8cd..bab258986aa5c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -208,6 +208,7 @@ package android { ctor public R.array(); field public static final int emailAddressTypes = 17235968; // 0x1070000 field public static final int imProtocols = 17235969; // 0x1070001 + field public static final int networkAttributes = 17235989; // 0x1070015 field public static final int organizationTypes = 17235970; // 0x1070002 field public static final int phoneTypes = 17235971; // 0x1070003 field public static final int postalAddressTypes = 17235972; // 0x1070004 diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index b24995d5da7bf..2c5562e78c1c3 100644 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -78,13 +78,15 @@ private void run(String[] args) { return; } } else if (command.equals("touchscreen") || command.equals("touchpad") - || command.equals("touchnavigation")) { + || command.equals("touchnavigation") || command.equals("gesture")) { // determine input source int inputSource = InputDevice.SOURCE_TOUCHSCREEN; if (command.equals("touchpad")) { inputSource = InputDevice.SOURCE_TOUCHPAD; } else if (command.equals("touchnavigation")) { inputSource = InputDevice.SOURCE_TOUCH_NAVIGATION; + } else if (command.equals("gesture")) { + inputSource = InputDevice.SOURCE_GESTURE_SENSOR; } // determine subcommand if (args.length > 1) { diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java index d29e8b22dcc7f..d776f59303264 100644 --- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java +++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java @@ -65,7 +65,7 @@ public void run(String[] args) { IWifiManager wifiMgr = IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE)); try { - wifiMgr.setWifiEnabled(flag); + wifiMgr.setWifiEnabled("com.android.commands.svc", flag); } catch (RemoteException e) { System.err.println("Wi-Fi operation failed: " + e); @@ -75,4 +75,4 @@ public void run(String[] args) { } System.err.println(longHelp()); } -} \ No newline at end of file +} diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index d7be2b811977a..d5b3303e2c428 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,6 +62,7 @@ public class AppOpsManager { public static final int MODE_ALLOWED = 0; public static final int MODE_IGNORED = 1; public static final int MODE_ERRORED = 2; + public static final int MODE_ASK = 3; // when adding one of these: // - increment _NUM_OP @@ -97,7 +101,55 @@ public class AppOpsManager { public static final int OP_READ_CLIPBOARD = 29; public static final int OP_WRITE_CLIPBOARD = 30; /** @hide */ - public static final int _NUM_OP = 31; + public static final int OP_WIFI_CHANGE = 31; + public static final int OP_BLUETOOTH_CHANGE = 32; + public static final int OP_DATA_CONNECT_CHANGE = 33; + public static final int OP_ALARM_WAKEUP = 34; + public static final int _NUM_OP = 35; + + /** + * Map to check if each operation is strict or not, to determine default + * value of each operation. + * If strict then AppOpsService should assign MODE_ASK value to operation + * by default. + */ + private static boolean[] sOpStrict = new boolean[] { + true, //OP_COARSE_LOCATION + true, //OP_FINE_LOCATION + true, //OP_GPS + false, //OP_VIBRATE + true, //OP_READ_CONTACTS + true, //OP_WRITE_CONTACTS + true, //OP_READ_CALL_LOG + true, //OP_WRITE_CALL_LOG + false, //OP_READ_CALENDAR + false, //OP_WRITE_CALENDAR + true, //OP_WIFI_SCAN + false, //OP_POST_NOTIFICATION + false, //OP_NEIGHBORING_CELLS + true, //OP_CALL_PHONE + true, //OP_READ_SMS + true, //OP_WRITE_SMS + true, //OP_RECEIVE_SMS + false, //OP_RECEIVE_EMERGECY_SMS + true, //OP_RECEIVE_MMS + false, //OP_RECEIVE_WAP_PUSH + true, //OP_SEND_SMS + true, //OP_READ_ICC_SMS + true, //OP_WRITE_ICC_SMS + false, //OP_WRITE_SETTINGS + false, //OP_SYSTEM_ALERT_WINDOW + false, //OP_ACCESS_NOTIFICATIONS + true, //OP_CAMERA + true, //OP_RECORD_AUDIO + true, //OP_PLAY_AUDIO + false, //OP_READ_CLIPBOARD + false, //OP_WRITE_CLIPBOARD + true, //OP_WIFI_CHANGE + true, //OP_BLUETOOTH_CHANGE + true, //OP_DATA_CONNECT_CHANGE + false, //OP_ALARM_WAKEUP + }; /** * This maps each operation to the operation that serves as the @@ -139,6 +191,10 @@ public class AppOpsManager { OP_PLAY_AUDIO, OP_READ_CLIPBOARD, OP_WRITE_CLIPBOARD, + OP_WIFI_CHANGE, + OP_BLUETOOTH_CHANGE, + OP_DATA_CONNECT_CHANGE, + OP_ALARM_WAKEUP, }; /** @@ -177,6 +233,10 @@ public class AppOpsManager { "PLAY_AUDIO", "READ_CLIPBOARD", "WRITE_CLIPBOARD", + "WIFI_CHANGE", + "BLUETOOTH_CHANGE", + "DATA_CONNECT_CHANGE", + "ALARM_WAKEUP", }; /** @@ -215,8 +275,19 @@ public class AppOpsManager { null, // no permission for playing audio null, // no permission for reading clipboard null, // no permission for writing clipboard + android.Manifest.permission.CHANGE_WIFI_STATE, + android.Manifest.permission.BLUETOOTH, + android.Manifest.permission.CHANGE_NETWORK_STATE, + null, // no permission for alarm wakeups }; + /** + * Check if given operation is strict or not. + */ + public static boolean opStrict(int op) { + return sOpStrict[op]; + } + /** * Retrieve the op switch that controls the given operation. */ @@ -232,6 +303,18 @@ public static String opToName(int op) { return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")"); } + /** + * Map a non-localized name for the operation back to the Op number + */ + public static int nameToOp(String name) { + for (int i = 0; i < sOpNames.length; i++) { + if (sOpNames[i].equals(name)) { + return i; + } + } + return OP_NONE; + } + /** * Retrieve the permission associated with an operation, or null if there is not one. */ @@ -310,13 +393,18 @@ public static class OpEntry implements Parcelable { private final long mTime; private final long mRejectTime; private final int mDuration; + private final int mAllowedCount; + private final int mIgnoredCount; - public OpEntry(int op, int mode, long time, long rejectTime, int duration) { + public OpEntry(int op, int mode, long time, long rejectTime, int duration, + int allowedCount, int ignoredCount) { mOp = op; mMode = mode; mTime = time; mRejectTime = rejectTime; mDuration = duration; + mAllowedCount = allowedCount; + mIgnoredCount = ignoredCount; } public int getOp() { @@ -343,6 +431,14 @@ public int getDuration() { return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration; } + public int getAllowedCount() { + return mAllowedCount; + } + + public int getIgnoredCount() { + return mIgnoredCount; + } + @Override public int describeContents() { return 0; @@ -355,6 +451,8 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mTime); dest.writeLong(mRejectTime); dest.writeInt(mDuration); + dest.writeInt(mAllowedCount); + dest.writeInt(mIgnoredCount); } OpEntry(Parcel source) { @@ -363,6 +461,8 @@ public void writeToParcel(Parcel dest, int flags) { mTime = source.readLong(); mRejectTime = source.readLong(); mDuration = source.readInt(); + mAllowedCount = source.readInt(); + mIgnoredCount = source.readInt(); } public static final Creator CREATOR = new Creator() { @@ -555,4 +655,11 @@ public void setPrivacyGuardSettingForPackage(int uid, String packageName, } catch (RemoteException e) { } } + + public void resetCounters() { + try { + mService.resetCounters(); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 3a63a2a82bcb4..417785501c16e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1,6 +1,8 @@ /* * Copyright (C) 2006 The Android Open Source Project * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -98,6 +100,7 @@ import android.os.SystemVibrator; import android.os.UserManager; import android.os.storage.StorageManager; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import android.content.ClipboardManager; import android.util.AndroidRuntimeException; @@ -489,6 +492,13 @@ public Object createService(ContextImpl ctx) { return new TelephonyManager(ctx.getOuterContext()); }}); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + registerService(MSIM_TELEPHONY_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new MSimTelephonyManager(ctx.getOuterContext()); + }}); + } + registerService(UI_MODE_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new UiModeManager(); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 229f41f0977fc..e23e4152871be 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.ActivityThread; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -516,7 +519,7 @@ public boolean enable() { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -1181,9 +1184,15 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener } else if (profile == BluetoothProfile.SAP) { BluetoothSap sap = new BluetoothSap(context, listener); return true; + } else if (profile == BluetoothProfile.DUN) { + BluetoothDun dun = new BluetoothDun(context, listener); + return true; } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; + } else if (profile == BluetoothProfile.HANDSFREE_CLIENT) { + BluetoothHandsfreeClient hfpclient = new BluetoothHandsfreeClient(context, listener); + return true; } else { return false; } @@ -1224,6 +1233,10 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { BluetoothSap sap = (BluetoothSap)proxy; sap.close(); break; + case BluetoothProfile.DUN: + BluetoothDun dun = (BluetoothDun)proxy; + dun.close(); + break; case BluetoothProfile.HEALTH: BluetoothHealth health = (BluetoothHealth)proxy; health.close(); @@ -1236,6 +1249,10 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { BluetoothGattServer gattServer = (BluetoothGattServer)proxy; gattServer.close(); break; + case BluetoothProfile.HANDSFREE_CLIENT: + BluetoothHandsfreeClient hfpclient = (BluetoothHandsfreeClient)proxy; + hfpclient.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothDun.java b/core/java/android/bluetooth/BluetoothDun.java new file mode 100644 index 0000000000000..37fc34a4299a3 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothDun.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package android.bluetooth; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the APIs to control the Bluetooth Dun + * Profile. + * + *

BluetoothDun is a proxy object for controlling the Bluetooth DUN + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothDun proxy object. + * + *

Each method is protected with its appropriate permission. + *@hide + */ +public final class BluetoothDun implements BluetoothProfile { + private static final String TAG = "BluetoothDun"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the Dun + * profile. + * + *

This intent will have 3 extras: + *

+ * + *

{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTED}. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "codeaurora.bluetooth.dun.profile.action.CONNECTION_STATE_CHANGED"; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothDun mDunService; + + /** + * Create a BluetoothDun proxy object for interacting with the local + * Bluetooth Service which handles the Dun profile + * + */ + /*package*/ BluetoothDun(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + try { + mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + } + Log.d(TAG, "BluetoothDun() call bindService"); + if (!context.bindService(new Intent(IBluetoothDun.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth DUN Service"); + } + Log.d(TAG, "BluetoothDun(), bindService called"); + } + + /*package*/ void close() { + if (VDBG) log("close()"); + mServiceListener = null; + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re); + } + } + + synchronized (mConnection) { + if ( mDunService != null) { + try { + mDunService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + + protected void finalize() { + close(); + } + + private IBluetoothStateChangeCallback mStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + @Override + public void onBluetoothStateChange(boolean on) throws RemoteException { + //Handle enable request to bind again. + if (on) { + Log.d(TAG, "onBluetoothStateChange(on) call bindService"); + if (!mContext.bindService(new Intent(IBluetoothDun.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth DUN Service"); + } + Log.d(TAG, "BluetoothDun(), bindService called"); + } else { + if (VDBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + if ( mDunService != null) { + try { + mDunService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + } + }; + + /** + * Initiate disconnection from DUN server. + * + *

Once the disconnection is initiated by any device either local host + * or remote device, the state will transition from {@link #STATE_CONNECTED} + * to {@link #STATE_DISCONNECTED}. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mDunService != null && isEnabled() && + isValidDevice(device)) { + try { + return mDunService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** + * {@inheritDoc} + */ + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + if (mDunService != null && isEnabled()) { + try { + return mDunService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + if (mDunService != null && isEnabled()) { + try { + return mDunService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + if (mDunService != null && isEnabled() + && isValidDevice(device)) { + try { + return mDunService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "BluetoothDUN Proxy object connected"); + mDunService = IBluetoothDun.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.DUN, + BluetoothDun.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "BluetoothDUN Proxy object disconnected"); + mDunService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.DUN); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothHandsfreeClient.java b/core/java/android/bluetooth/BluetoothHandsfreeClient.java new file mode 100644 index 0000000000000..dd4212a6825fd --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHandsfreeClient.java @@ -0,0 +1,1158 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * 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 android.bluetooth; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Public API to control Hands Free Profile (HFP role only). + *

+ * This class defines methods that shall be used by application to manage profile + * connection, calls states and calls actions. + *

+ * + * @hide + * */ +public final class BluetoothHandsfreeClient implements BluetoothProfile { + private static final String TAG = "BluetoothHandsfreeClient"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** + * Intent sent whenever connection to remote changes. + * + *

It includes two extras: + * BluetoothProfile.EXTRA_PREVIOUS_STATE + * and BluetoothProfile.EXTRA_STATE, which + * are mandatory. + *

There are also non mandatory feature extras: + * {@link #EXTRA_AG_FEATURE_3WAY_CALLING}, + * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}, + * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}, + * {@link #EXTRA_AG_FEATURE_REJECT_CALL}, + * {@link #EXTRA_AG_FEATURE_ECC}, + * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD}, + * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL}, + * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL}, + * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT}, + * {@link #EXTRA_AG_FEATURE_MERGE}, + * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}, + * sent as boolean values only when EXTRA_STATE + * is set to STATE_CONNECTED.

+ * + *

Note that features supported by AG are being sent as + * booleans with value true, + * and not supported ones are not being sent at all.

+ */ + public static final String ACTION_CONNECTION_STATE_CHANGED = + "org.codeaurora.handsfreeclient.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent sent whenever audio state changes. + * + *

It includes two mandatory extras: + * {@link BluetoothProfile.EXTRA_STATE}, + * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE}, + * with possible values: + * {@link #STATE_AUDIO_CONNECTING}, + * {@link #STATE_AUDIO_CONNECTED}, + * {@link #STATE_AUDIO_DISCONNECTED}

+ *

When EXTRA_STATE is set + * to STATE_AUDIO_CONNECTED, + * it also includes {@link #EXTRA_AUDIO_WBS} + * indicating wide band speech support.

+ */ + public static final String ACTION_AUDIO_STATE_CHANGED = + "org.codeaurora.handsfreeclient.profile.action.AUDIO_STATE_CHANGED"; + + /** + * Intent sending updates of the Audio Gateway state. + * Each extra is being sent only when value it + * represents has been changed recently on AG. + *

It can contain one or more of the following extras: + * {@link #EXTRA_NETWORK_STATUS}, + * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH}, + * {@link #EXTRA_NETWORK_ROAMING}, + * {@link #EXTRA_BATTERY_LEVEL}, + * {@link #EXTRA_OPERATOR_NAME}, + * {@link #EXTRA_VOICE_RECOGNITION}, + * {@link #EXTRA_IN_BAND_RING}

+ */ + public static final String ACTION_AG_EVENT = + "org.codeaurora.handsfreeclient.profile.action.AG_EVENT"; + + /** + * Intent sent whenever state of a call changes. + * + *

It includes: + * {@link #EXTRA_CALL}, + * with value of {@link BluetoothHandsfreeClientCall} instance, + * representing actual call state.

+ */ + public static final String ACTION_CALL_CHANGED = + "org.codeaurora.handsfreeclient.profile.action.AG_CALL_CHANGED"; + + /** + * Intent that notifies about the result of the last issued action. + * Please note that not every action results in explicit action result code being sent. + * Instead other notifications about new Audio Gateway state might be sent, + * like ACTION_AG_EVENT with EXTRA_VOICE_RECOGNITION value + * when for example user started voice recognition from HF unit. + */ + public static final String ACTION_RESULT = + "org.codeaurora.handsfreeclient.profile.action.RESULT"; + + /** + * Intent that notifies about the number attached to the last voice tag + * recorded on AG. + * + *

It contains: + * {@link #EXTRA_NUMBER}, + * with a String value representing phone number.

+ */ + public static final String ACTION_LAST_VTAG = + "org.codeaurora.handsfreeclient.profile.action.LAST_VTAG"; + + public static final int STATE_AUDIO_DISCONNECTED = 0; + public static final int STATE_AUDIO_CONNECTING = 1; + public static final int STATE_AUDIO_CONNECTED = 2; + + /** + * Extra with information if connected audio is WBS. + *

Possible values: true, + * false.

+ */ + public static final String EXTRA_AUDIO_WBS = + "android.bluetooth.handsfreeclient.extra.AUDIO_WBS"; + + /** + * Extra for AG_EVENT indicates network status. + *

Value: 0 - network unavailable, + * 1 - network available

+ */ + public static final String EXTRA_NETWORK_STATUS = + "android.bluetooth.handsfreeclient.extra.NETWORK_STATUS"; + /** + * Extra for AG_EVENT intent indicates network signal strength. + *

Value: Integer representing signal strength.

+ */ + public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = + "android.bluetooth.handsfreeclient.extra.NETWORK_SIGNAL_STRENGTH"; + /** + * Extra for AG_EVENT intent indicates roaming state. + *

Value: 0 - no roaming + * 1 - active roaming

+ */ + public static final String EXTRA_NETWORK_ROAMING = + "android.bluetooth.handsfreeclient.extra.NETWORK_ROAMING"; + /** + * Extra for AG_EVENT intent indicates the battery level. + *

Value: Integer representing signal strength.

+ */ + public static final String EXTRA_BATTERY_LEVEL = + "android.bluetooth.handsfreeclient.extra.BATTERY_LEVEL"; + /** + * Extra for AG_EVENT intent indicates operator name. + *

Value: String representing operator name.

+ */ + public static final String EXTRA_OPERATOR_NAME = + "android.bluetooth.handsfreeclient.extra.OPERATOR_NAME"; + /** + * Extra for AG_EVENT intent indicates voice recognition state. + *

Value: + * 0 - voice recognition stopped, + * 1 - voice recognition started.

+ */ + public static final String EXTRA_VOICE_RECOGNITION = + "android.bluetooth.handsfreeclient.extra.VOICE_RECOGNITION"; + /** + * Extra for AG_EVENT intent indicates in band ring state. + *

Value: + * 0 - in band ring tone not supported, or + * 1 - in band ring tone supported.

+ */ + public static final String EXTRA_IN_BAND_RING = + "android.bluetooth.handsfreeclient.extra.IN_BAND_RING"; + + /** + * Extra for AG_EVENT intent indicates subscriber info. + *

Value: String containing subscriber information.

+ */ + public static final String EXTRA_SUBSCRIBER_INFO = + "android.bluetooth.handsfreeclient.extra.SUBSCRIBER_INFO"; + + /** + * Extra for AG_CALL_CHANGED intent indicates the + * {@link BluetoothHandsfreeClientCall} object that has changed. + */ + public static final String EXTRA_CALL = + "android.bluetooth.handsfreeclient.extra.CALL"; + + /** + * Extra for ACTION_LAST_VTAG intent. + *

Value: String representing phone number + * corresponding to last voice tag recorded on AG

+ */ + public static final String EXTRA_NUMBER = + "android.bluetooth.handsfreeclient.extra.NUMBER"; + + /** + * Extra for ACTION_RESULT intent that shows the result code of + * last issued action. + *

Possible results: + * {@link #ACTION_RESULT_OK}, + * {@link #ACTION_RESULT_ERROR}, + * {@link #ACTION_RESULT_ERROR_NO_CARRIER}, + * {@link #ACTION_RESULT_ERROR_BUSY}, + * {@link #ACTION_RESULT_ERROR_NO_ANSWER}, + * {@link #ACTION_RESULT_ERROR_DELAYED}, + * {@link #ACTION_RESULT_ERROR_BLACKLISTED}, + * {@link #ACTION_RESULT_ERROR_CME}

+ */ + public static final String EXTRA_RESULT_CODE = + "android.bluetooth.handsfreeclient.extra.RESULT_CODE"; + + /** + * Extra for ACTION_RESULT intent that shows the extended result code of + * last issued action. + *

Value: Integer - error code.

+ */ + public static final String EXTRA_CME_CODE = + "android.bluetooth.handsfreeclient.extra.CME_CODE"; + + /* Extras for AG_FEATURES, extras type is boolean */ + // TODO verify if all of those are actually useful + /** + * AG feature: three way calling. + */ + public final static String EXTRA_AG_FEATURE_3WAY_CALLING = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; + /** + * AG feature: voice recognition. + */ + public final static String EXTRA_AG_FEATURE_VOICE_RECOGNITION = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; + /** + * AG feature: fetching phone number for voice tagging procedure. + */ + public final static String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; + /** + * AG feature: ability to reject incoming call. + */ + public final static String EXTRA_AG_FEATURE_REJECT_CALL = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; + /** + * AG feature: enhanced call handling (terminate specific call, private consultation). + */ + public final static String EXTRA_AG_FEATURE_ECC = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ECC"; + /** + * AG feature: response and hold. + */ + public final static String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; + /** + * AG call handling feature: accept held or waiting call in three way calling scenarios. + */ + public final static String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; + /** + * AG call handling feature: release held or waiting call in three way calling scenarios. + */ + public final static String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; + /** + * AG call handling feature: release active call and accept held or waiting call in three way calling scenarios. + */ + public final static String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; + /** + * AG call handling feature: merge two calls, held and active - multi party conference mode. + */ + public final static String EXTRA_AG_FEATURE_MERGE = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_MERGE"; + /** + * AG call handling feature: merge calls and disconnect from multi party + * conversation leaving peers connected to each other. + * Note that this feature needs to be supported by mobile network operator + * as it requires connection and billing transfer. + */ + public final static String EXTRA_AG_FEATURE_MERGE_AND_DETACH = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; + + /* Action result codes */ + public final static int ACTION_RESULT_OK = 0; + public final static int ACTION_RESULT_ERROR = 1; + public final static int ACTION_RESULT_ERROR_NO_CARRIER = 2; + public final static int ACTION_RESULT_ERROR_BUSY = 3; + public final static int ACTION_RESULT_ERROR_NO_ANSWER = 4; + public final static int ACTION_RESULT_ERROR_DELAYED = 5; + public final static int ACTION_RESULT_ERROR_BLACKLISTED = 6; + public final static int ACTION_RESULT_ERROR_CME = 7; + + /* Detailed CME error codes */ + public final static int CME_PHONE_FAILURE = 0; + public final static int CME_NO_CONNECTION_TO_PHONE = 1; + public final static int CME_OPERATION_NOT_ALLOWED = 3; + public final static int CME_OPERATION_NOT_SUPPORTED = 4; + public final static int CME_PHSIM_PIN_REQUIRED = 5; + public final static int CME_PHFSIM_PIN_REQUIRED = 6; + public final static int CME_PHFSIM_PUK_REQUIRED = 7; + public final static int CME_SIM_NOT_INSERTED = 10; + public final static int CME_SIM_PIN_REQUIRED = 11; + public final static int CME_SIM_PUK_REQUIRED = 12; + public final static int CME_SIM_FAILURE = 13; + public final static int CME_SIM_BUSY = 14; + public final static int CME_SIM_WRONG = 15; + public final static int CME_INCORRECT_PASSWORD = 16; + public final static int CME_SIM_PIN2_REQUIRED = 17; + public final static int CME_SIM_PUK2_REQUIRED = 18; + public final static int CME_MEMORY_FULL = 20; + public final static int CME_INVALID_INDEX = 21; + public final static int CME_NOT_FOUND = 22; + public final static int CME_MEMORY_FAILURE = 23; + public final static int CME_TEXT_STRING_TOO_LONG = 24; + public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; + public final static int CME_DIAL_STRING_TOO_LONG = 26; + public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; + public final static int CME_NO_NETWORK_SERVICE = 30; + public final static int CME_NETWORK_TIMEOUT = 31; + public final static int CME_EMERGENCY_SERVICE_ONLY = 32; + public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; + public final static int CME_NOT_SUPPORTED_FOR_VOIP = 34; + public final static int CME_SIP_RESPONSE_CODE = 35; + public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; + public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; + public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; + public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; + public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; + public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; + public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; + public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; + public final static int CME_HIDDEN_KEY_REQUIRED = 48; + public final static int CME_EAP_NOT_SUPPORTED = 49; + public final static int CME_INCORRECT_PARAMETERS = 50; + + /* Action policy for other calls when accepting call */ + public static final int CALL_ACCEPT_NONE = 0; + public static final int CALL_ACCEPT_HOLD = 1; + public static final int CALL_ACCEPT_TERMINATE = 2; + + private Context mContext; + private ServiceListener mServiceListener; + private IBluetoothHandsfreeClient mService; + private BluetoothAdapter mAdapter; + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + @Override + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (VDBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (VDBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothHandsfreeClient.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Handsfree Client Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + + /** + * Create a BluetoothHandsfreeClient proxy object. + */ + /*package*/ BluetoothHandsfreeClient(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + if (!context.bindService(new Intent(IBluetoothHandsfreeClient.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Handsfree Client Service"); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothHandsfreeClient will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + /*package*/ void close() { + if (VDBG) log("close()"); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (Exception e) { + Log.e(TAG,"",e); + } + } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + mServiceListener = null; + } + + /** + * Connects to remote device. + * + * Currently, the system supports only 1 connection. So, in case of the + * second connection, this implementation will disconnect already connected + * device automatically and will process the new one. + * + * @param device a remote device we want connect to + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} + * intent. + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connect(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Disconnects remote device + * + * @param device a remote device we want disconnect + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} + * intent. + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Return the list of connected remote devices + * + * @return list of connected devices; empty list if nothing is connected. + */ + @Override + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * Returns list of remote devices in a particular state + * + * @param states collection of states + * @return list of devices that state matches the states listed in + * states; empty list if nothing matches the + * states + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * Returns state of the device + * + * @param device a remote device + * @return the state of connection of the device + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getConnectionState(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Set priority of the profile + * + * The device should already be paired. + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return mService.setPriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the priority of the profile. + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return PRIORITY_OFF; + } + + /** + * Starts voice recognition. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AG_EVENT} + * intent. + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean startVoiceRecognition(BluetoothDevice device) { + if (DBG) log("startVoiceRecognition()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.startVoiceRecognition(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Stops voice recognition. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AG_EVENT} + * intent. + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean stopVoiceRecognition(BluetoothDevice device) { + if (DBG) log("stopVoiceRecognition()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.stopVoiceRecognition(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Returns list of all calls in any state. + * + * @param device remote device + * @return list of calls; empty list if none call exists + */ + public List getCurrentCalls(BluetoothDevice device) { + if (DBG) log("getCurrentCalls()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getCurrentCalls(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + /** + * Returns list of current values of AG indicators. + * + * @param device remote device + * @return bundle of AG indicators; null if device is not in + * CONNECTED state + */ + public Bundle getCurrentAgEvents(BluetoothDevice device) { + if (DBG) log("getCurrentCalls()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getCurrentAgEvents(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + /** + * Accepts a call + * + * @param device remote device + * @param flag action policy while accepting a call. Possible values + * {@link #CALL_ACCEPT_NONE}, {@link #CALL_ACCEPT_HOLD}, + * {@link #CALL_ACCEPT_TERMINATE} + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + */ + public boolean acceptCall(BluetoothDevice device, int flag) { + if (DBG) log("acceptCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.acceptCall(device, flag); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Holds a call. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + */ + public boolean holdCall(BluetoothDevice device) { + if (DBG) log("holdCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.holdCall(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Rejects a call. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_REJECT_CALL}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean rejectCall(BluetoothDevice device) { + if (DBG) log("rejectCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.rejectCall(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Terminates a specified call. + * + * Works only when Extended Call Control is supported by Audio Gateway. + * + * @param device remote device + * @param index index of the call to be terminated + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_ECC}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean terminateCall(BluetoothDevice device, int index) { + if (DBG) log("terminateCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.terminateCall(device, index); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Enters private mode with a specified call. + * + * Works only when Extended Call Control is supported by Audio Gateway. + * + * @param device remote device + * @param index index of the call to connect in private mode + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_ECC}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean enterPrivateMode(BluetoothDevice device, int index) { + if (DBG) log("enterPrivateMode()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.enterPrivateMode(device, index); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Performs explicit call transfer. + * + * That means connect other calls and disconnect. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean explicitCallTransfer(BluetoothDevice device) { + if (DBG) log("explicitCallTransfer()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.explicitCallTransfer(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Redials last number from Audio Gateway. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; + */ + public boolean redial(BluetoothDevice device) { + if (DBG) log("redial()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.redial(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Places a call with specified number. + * + * @param device remote device + * @param number valid phone number + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; + */ + public boolean dial(BluetoothDevice device, String number) { + if (DBG) log("dial()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.dial(device, number); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Places a call to the number under specified memory location. + * + * @param device remote device + * @param location valid memory location + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; + */ + public boolean dialMemory(BluetoothDevice device, int location) { + if (DBG) log("dialMemory()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.dialMemory(device, location); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Sends DTMF code. + * + * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,# + * + * @param device remote device + * @param code ASCII code + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_RESULT} intent; + */ + public boolean sendDTMF(BluetoothDevice device, byte code) { + if (DBG) log("sendDTMF()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.sendDTMF(device, code); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get a number corresponding to last voice tag recorded on AG. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_LAST_VTAG} + * or {@link #ACTION_RESULT} intent; + * + *

Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. + * This method invocation will fail silently when feature is not supported.

+ */ + public boolean getLastVoiceTagNumber(BluetoothDevice device) { + if (DBG) log("getLastVoiceTagNumber()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getLastVoiceTagNumber(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Accept the incoming connection. + */ + public boolean acceptIncomingConnect(BluetoothDevice device) { + if (DBG) log("acceptIncomingConnect"); + if (mService != null && isEnabled()) { + 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; + } + + /** + * Reject the incoming connection. + */ + public boolean rejectIncomingConnect(BluetoothDevice device) { + if (DBG) log("rejectIncomingConnect"); + if (mService != null) { + try { + return mService.rejectIncomingConnect(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; + } + + /** + * Returns current audio state of Audio Gateway. + * + * Note: This is an internal function and shouldn't be exposed + */ + public int getAudioState(BluetoothDevice device) { + if (VDBG) log("getAudioState"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioState(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 BluetoothHandsfreeClient.STATE_AUDIO_DISCONNECTED; + } + + /** + * Initiates a connection of audio channel. + * + * It setup SCO channel with remote connected Handsfree AG device. + * + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} + * intent; + */ + public boolean connectAudio() { + if (mService != null && isEnabled()) { + try { + return mService.connectAudio(); + } 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; + } + + /** + * Disconnects audio channel. + * + * It tears down the SCO channel from remote AG device. + * + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} + * intent; + */ + public boolean disconnectAudio() { + if (mService != null && isEnabled()) { + try { + return mService.disconnectAudio(); + } 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; + } + + /** + * Get Audio Gateway features + * + * @param device remote device + * @return bundle of AG features; null if no service or + * AG not connected + */ + public Bundle getCurrentAgFeatures(BluetoothDevice device) { + if (mService != null && isEnabled()) { + try { + return mService.getCurrentAgFeatures(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 null; + } + + + private ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothHandsfreeClient.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HANDSFREE_CLIENT, BluetoothHandsfreeClient.this); + } + } + @Override + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HANDSFREE_CLIENT); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl b/core/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl new file mode 100644 index 0000000000000..ca90385a8dcb3 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * 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 android.bluetooth; + +parcelable BluetoothHandsfreeClientCall; diff --git a/core/java/android/bluetooth/BluetoothHandsfreeClientCall.java b/core/java/android/bluetooth/BluetoothHandsfreeClientCall.java new file mode 100644 index 0000000000000..872ab6be0c9b6 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHandsfreeClientCall.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package android.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class represents a single call, its state and properties. + * It implements {@link Parcelable} for inter-process message passing. + * @hide + */ +public final class BluetoothHandsfreeClientCall implements Parcelable { + + /* Call state */ + /** + * Call is active. + */ + public static final int CALL_STATE_ACTIVE = 0; + /** + * Call is in held state. + */ + public static final int CALL_STATE_HELD = 1; + /** + * Outgoing call that is being dialed right now. + */ + public static final int CALL_STATE_DIALING = 2; + /** + * Outgoing call that remote party has already been alerted about. + */ + public static final int CALL_STATE_ALERTING = 3; + /** + * Incoming call that can be accepted or rejected. + */ + public static final int CALL_STATE_INCOMING = 4; + /** + * Waiting call state when there is already an active call. + */ + public static final int CALL_STATE_WAITING = 5; + /** + * Call that has been held by response and hold + * (see Bluetooth specification for further references). + */ + public static final int CALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6; + /** + * Call that has been already terminated and should not be referenced as a valid call. + */ + public static final int CALL_STATE_TERMINATED = 7; + + private final int mId; + private int mState; + private String mNumber; + private boolean mMultiParty; + private final boolean mOutgoing; + + /** + * Creates BluetoothHandsfreeClientCall instance. + */ + public BluetoothHandsfreeClientCall(int id, int state, String number, boolean multiParty, + boolean outgoing) { + mId = id; + mState = state; + mNumber = number != null ? number : ""; + mMultiParty = multiParty; + mOutgoing = outgoing; + } + + /** + * Sets call's state. + * + *

Note: This is an internal function and shouldn't be exposed

+ * + * @param state new call state. + */ + public void setState(int state) { + mState = state; + } + + /** + * Sets call's number. + * + *

Note: This is an internal function and shouldn't be exposed

+ * + * @param number String representing phone number. + */ + public void setNumber(String number) { + mNumber = number; + } + + /** + * Sets this call as multi party call. + * + *

Note: This is an internal function and shouldn't be exposed

+ * + * @param multiParty if true sets this call as a part + * of multi party conference. + */ + public void setMultiParty(boolean multiParty) { + mMultiParty = multiParty; + } + + /** + * Gets call's Id. + * + * @return call id. + */ + public int getId() { + return mId; + } + + /** + * Gets call's current state. + * + * @return state of this particular phone call. + */ + public int getState() { + return mState; + } + + /** + * Gets call's number. + * + * @return string representing phone number. + */ + public String getNumber() { + return mNumber; + } + + /** + * Checks if call is an active call in a conference mode (aka multi party). + * + * @return true if call is a multi party call, + * false otherwise. + */ + public boolean isMultiParty() { + return mMultiParty; + } + + /** + * Checks if this call is an outgoing call. + * + * @return true if its outgoing call, + * false otherwise. + */ + public boolean isOutgoing() { + return mOutgoing; + } + + /** + * {@link Parcelable.Creator} interface implementation. + */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public BluetoothHandsfreeClientCall createFromParcel(Parcel in) { + return new BluetoothHandsfreeClientCall(in.readInt(), in.readInt(), + in.readString(), in.readInt() == 1, in.readInt() == 1); + } + + @Override + public BluetoothHandsfreeClientCall[] newArray(int size) { + return new BluetoothHandsfreeClientCall[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mId); + out.writeInt(mState); + out.writeString(mNumber); + out.writeInt(mMultiParty ? 1 : 0); + out.writeInt(mOutgoing ? 1 : 0); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 7c2ea6e466180..f92f83dc78e4e 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -103,6 +105,18 @@ public interface BluetoothProfile { */ public static final int SAP = 20; + /** + * Handsfree Client - HFP HF Role + * @hide + */ + public static final int HANDSFREE_CLIENT = 9; + + /** + * DUN + * @hide + */ + public static final int DUN = 21; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index a19341c07da41..375b24af751b8 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -200,6 +200,7 @@ private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { as.mSocketOS = as.mSocket.getOutputStream(); as.mAddress = RemoteAddr; as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); + as.mPort = mPort; return as; } /** @@ -404,6 +405,61 @@ public void connect() throws IOException { return acceptedSocket; } + /** + * setSocketOpt for the Buetooth Socket. + * + * @param optionName socket option name + * @param optionVal socket option value + * @param optionLen socket option length + * @return -1 on immediate error, + * 0 otherwise + * @hide + */ + public int setSocketOpt(int optionName, byte [] optionVal, int optionLen) throws IOException { + int ret = 0; + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + Log.e(TAG, "setSocketOpt fail, reason: bluetooth is off"); + return -1; + } + try { + if(VDBG) Log.d(TAG, "setSocketOpt(), mType: " + mType + " mPort: " + mPort); + ret = bluetoothProxy.setSocketOpt(mType, mPort, optionName, optionVal, optionLen); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return -1; + } + return ret; + } + + /** + * getSocketOpt for the Buetooth Socket. + * + * @param optionName socket option name + * @param optionVal socket option value + * @return -1 on immediate error, + * length of returned socket option otherwise + * @hide + */ + public int getSocketOpt(int optionName, byte [] optionVal) throws IOException { + int ret = 0; + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + Log.e(TAG, "getSocketOpt fail, reason: bluetooth is off"); + return -1; + } + try { + if(VDBG) Log.d(TAG, "getSocketOpt(), mType: " + mType + " mPort: " + mPort); + ret = bluetoothProxy.getSocketOpt(mType, mPort, optionName, optionVal); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return -1; + } + return ret; + } + /*package*/ int available() throws IOException { if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 1f6f801cc6340..043387feb99a5 100755 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -85,4 +85,6 @@ interface IBluetooth // For Socket ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); + int setSocketOpt(int type, int port, int optionName, in byte [] optionVal, int optionLen); + int getSocketOpt(int type, int port, int optionName, out byte [] optionVal); } diff --git a/core/java/android/bluetooth/IBluetoothDun.aidl b/core/java/android/bluetooth/IBluetoothDun.aidl new file mode 100755 index 0000000000000..23fc90d026d4b --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothDun.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * API for Bluetooth Dun service + * + * {@hide} + */ +interface IBluetoothDun { + // Public API + boolean disconnect(in BluetoothDevice device); + int getConnectionState(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); +} diff --git a/core/java/android/bluetooth/IBluetoothHandsfreeClient.aidl b/core/java/android/bluetooth/IBluetoothHandsfreeClient.aidl new file mode 100644 index 0000000000000..1e660ae1b18ee --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothHandsfreeClient.aidl @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * 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 android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHandsfreeClientCall; +import android.os.Bundle; + +/** + * API for Bluetooth Handsfree Client service (HFP HF Role) + * + * {@hide} + */ +interface IBluetoothHandsfreeClient { + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + + boolean acceptIncomingConnect(in BluetoothDevice device); + boolean rejectIncomingConnect(in BluetoothDevice device); + + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + + boolean startVoiceRecognition(in BluetoothDevice device); + boolean stopVoiceRecognition(in BluetoothDevice device); + + List getCurrentCalls(in BluetoothDevice device); + Bundle getCurrentAgEvents(in BluetoothDevice device); + + boolean acceptCall(in BluetoothDevice device, int flag); + boolean holdCall(in BluetoothDevice device); + boolean rejectCall(in BluetoothDevice device); + boolean terminateCall(in BluetoothDevice device, int index); + + boolean enterPrivateMode(in BluetoothDevice device, int index); + boolean explicitCallTransfer(in BluetoothDevice device); + + boolean redial(in BluetoothDevice device); + boolean dial(in BluetoothDevice device, String number); + boolean dialMemory(in BluetoothDevice device, int location); + + boolean sendDTMF(in BluetoothDevice device, byte code); + boolean getLastVoiceTagNumber(in BluetoothDevice device); + + int getAudioState(in BluetoothDevice device); + boolean connectAudio(); + boolean disconnectAudio(); + + Bundle getCurrentAgFeatures(in BluetoothDevice device); +} diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index 493d2f8e77418..50df75d0290c1 100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/java/android/bluetooth/IBluetoothManager.aidl @@ -33,7 +33,7 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String callingPackage); boolean enableNoAutoConnect(); boolean disable(boolean persist); IBluetoothGatt getBluetoothGatt(); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 78846c2395cea..0cd4c75e90b42 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1650,7 +1653,7 @@ public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver, * @hide like {@link #stopService(Intent)} but for a specific user. */ public abstract boolean stopServiceAsUser(Intent service, UserHandle user); - + /** * Connect to an application service, creating it if needed. This defines * a dependency between your application and the service. The given @@ -2135,6 +2138,17 @@ public abstract boolean startInstrumentation(ComponentName className, */ public static final String TELEPHONY_SERVICE = "phone"; + /** + * Use with {@link #getSystemService} to retrieve a + * {android.telephony.MSimTelephonyManager} for handling the management + * of the telephony features of the multi sim device. + * + * @see #getSystemService + * @see android.telephony.MSimTelephonyManager + * @hide + */ + public static final String MSIM_TELEPHONY_SERVICE = "phone_msim"; + /** * Use with {@link #getSystemService} to retrieve a * {@link android.text.ClipboardManager} for accessing and modifying diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index a1566dadbadd1..8175acf3a0ca4 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -45,6 +45,7 @@ public class PackageInfoLite implements Parcelable { */ public int recommendedInstallLocation; public int installLocation; + public boolean isTheme; public VerifierInfo[] verifiers; @@ -66,6 +67,7 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(versionCode); dest.writeInt(recommendedInstallLocation); dest.writeInt(installLocation); + dest.writeInt(isTheme ? 1 : 0); if (verifiers == null || verifiers.length == 0) { dest.writeInt(0); @@ -91,6 +93,7 @@ private PackageInfoLite(Parcel source) { versionCode = source.readInt(); recommendedInstallLocation = source.readInt(); installLocation = source.readInt(); + isTheme = source.readInt() == 1 ? true : false; final int verifiersLength = source.readInt(); if (verifiersLength == 0) { diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 057593a87444c..45b5b841c2502 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -205,12 +205,14 @@ public static class PackageLite { public final int versionCode; public final int installLocation; public final VerifierInfo[] verifiers; + public final boolean isTheme; public PackageLite(String packageName, int versionCode, - int installLocation, List verifiers) { + int installLocation, List verifiers, boolean isTheme) { this.packageName = packageName; this.versionCode = versionCode; this.installLocation = installLocation; + this.isTheme = isTheme; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); } } @@ -905,6 +907,7 @@ private static PackageLite parsePackageLite(Resources res, XmlPullParser parser, int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; int versionCode = 0; int numFound = 0; + for (int i = 0; i < attrs.getAttributeCount(); i++) { String attr = attrs.getAttributeName(i); if (attr.equals("installLocation")) { @@ -924,6 +927,8 @@ private static PackageLite parsePackageLite(Resources res, XmlPullParser parser, final int searchDepth = parser.getDepth() + 1; final List verifiers = new ArrayList(); + boolean isTheme = false; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -936,9 +941,14 @@ private static PackageLite parsePackageLite(Resources res, XmlPullParser parser, verifiers.add(verifier); } } + + if (parser.getDepth() == searchDepth && "theme".equals(parser.getName())) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + } } - return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers); + return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers, isTheme); } /** diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 7bf524662487d..37a70559e8958 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -869,7 +869,7 @@ public void handleMessage(Message msg) { case CAMERA_MSG_META_DATA: if (mCameraMetaDataCallback != null) { - mCameraMetaDataCallback.onCameraMetaData((int[])msg.obj, mCamera); + mCameraMetaDataCallback.onCameraMetaData((byte[])msg.obj, mCamera); } return; /* ### QC ADD-ONS: END */ @@ -1481,6 +1481,23 @@ public Face() { * as a set. Either they are all valid, or none of them are. */ public Point mouth = null; + + /** + * {@hide} + */ + public int smileDegree = 0; + /** + * {@hide} + */ + public int smileScore = 0; + /** + * {@hide} + */ + public int blinkDetected = 0; + /** + * {@hide} + */ + public int faceRecognised = 0; } // Error codes match the enum in include/ui/Camera.h @@ -1620,25 +1637,24 @@ public interface CameraMetaDataCallback { /** * Callback for when camera meta data is available. * - * @param data a int array of the camera meta data + * @param data a byte array of the camera meta data * @param camera the Camera service object */ - void onCameraMetaData(int[] data, Camera camera); + void onCameraMetaData(byte[] data, Camera camera); }; /** @hide - * Set camera face detection mode and registers a callback function to run. + * Set camera meta data and registers a callback function to run. * Only valid after startPreview() has been called. * * @param cb the callback to run */ - //TBD - public final void setFaceDetectionCb(CameraMetaDataCallback cb) + public final void setMetadataCb(CameraMetaDataCallback cb) { mCameraMetaDataCallback = cb; - native_setFaceDetectionCb(cb!=null); + native_setMetadataCb(cb!=null); } - private native final void native_setFaceDetectionCb(boolean mode); + private native final void native_setMetadataCb(boolean mode); /** @hide * Set camera face detection command to send meta data. @@ -1649,6 +1665,17 @@ public final void sendMetaData() } private native final void native_sendMetaData(); + /** @hide + * Configure longshot mode. Available only in ZSL. + * + * @param enable enable/disable this mode + */ + public final void setLongshot(boolean enable) + { + native_setLongshot(enable); + } + private native final void native_setLongshot(boolean enable); + /** @hide * Handles the Touch Co-ordinate. */ @@ -4075,6 +4102,7 @@ private boolean same(String s1, String s2) { private static final String KEY_QC_ZSL = "zsl"; private static final String KEY_QC_CAMERA_MODE = "camera-mode"; private static final String KEY_QC_VIDEO_HIGH_FRAME_RATE = "video-hfr"; + private static final String KEY_QC_VIDEO_HDR = "video-hdr"; /** @hide * KEY_QC_AE_BRACKET_HDR **/ @@ -4377,6 +4405,17 @@ public List getSupportedZSLModes() { return split(str); } + /** @hide + * Gets the supported Video HDR modes. + * + * @return a List of Video HDR_OFF/OFF string constants. null if + * Video HDR mode setting is not supported. + */ + public List getSupportedVideoHDRModes() { + String str = get(KEY_QC_VIDEO_HDR + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + /** @hide * Gets the supported HFR modes. * @@ -4853,6 +4892,24 @@ public void setVideoHighFrameRate(String hfr) { set(KEY_QC_VIDEO_HIGH_FRAME_RATE, hfr); } + /** @hide + * Gets the current Video HDR Mode. + * + * @return Video HDR mode value + */ + public String getVideoHDRMode() { + return get(KEY_QC_VIDEO_HDR); + } + + /** @hide + * Sets the current Video HDR Mode. + * + * @return null + */ + public void setVideoHDRMode(String videohdr) { + set(KEY_QC_VIDEO_HDR, videohdr); + } + /** @hide * Gets the current DENOISE setting. * diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 0a7a2e71b770a..4af09d4719c40 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2012 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +19,7 @@ import android.content.Context; import android.os.Handler; +import android.os.RemoteException; import android.util.SparseArray; import android.view.Display; @@ -64,6 +66,18 @@ public final class DisplayManager { public static final String EXTRA_WIFI_DISPLAY_STATUS = "android.hardware.display.extra.WIFI_DISPLAY_STATUS"; + /** + * @hide + */ + public static final String ACTION_REMOTE_DISPLAY_STATUS_CHANGED = + "android.hardware.display.action.REMOTE_DISPLAY_STATUS_CHANGED"; + + /** + * @hide + */ + public static final String EXTRA_REMOTE_DISPLAY_STATUS = + "android.hardware.display.extra.REMOTE_DISPLAY_STATUS"; + /** * Display category: Presentation displays. *

@@ -274,6 +288,13 @@ public WifiDisplayStatus getWifiDisplayStatus() { return mGlobal.getWifiDisplayStatus(); } + /** + * @hide + */ + public IRemoteDisplayAdapter getRemoteDisplayAdapter() { + return mGlobal.getRemoteDisplayAdapter(); + } + /** * Listens for changes in available display devices. */ diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index a8586814af6ed..5128a737125b3 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -315,6 +316,15 @@ public WifiDisplayStatus getWifiDisplayStatus() { } } + public IRemoteDisplayAdapter getRemoteDisplayAdapter() { + try { + return mDm.getRemoteDisplayAdapter(); + } + catch (RemoteException e) { + return null; + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/IDisplayDevice.aidl b/core/java/android/hardware/display/IDisplayDevice.aidl new file mode 100644 index 0000000000000..75e00da17285e --- /dev/null +++ b/core/java/android/hardware/display/IDisplayDevice.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +import android.view.Surface; + +/** + * @hide + */ +interface IDisplayDevice { + Surface createDisplaySurface(); + oneway void stop(); +} diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 79aad78d53941..d523824dd1e88 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +21,7 @@ import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; import android.view.DisplayInfo; +import android.hardware.display.IRemoteDisplayAdapter; /** @hide */ interface IDisplayManager { @@ -46,4 +48,6 @@ interface IDisplayManager { // No permissions required. WifiDisplayStatus getWifiDisplayStatus(); + + IRemoteDisplayAdapter getRemoteDisplayAdapter(); } diff --git a/core/java/android/hardware/display/IRemoteDisplayAdapter.aidl b/core/java/android/hardware/display/IRemoteDisplayAdapter.aidl new file mode 100644 index 0000000000000..46892b68980e1 --- /dev/null +++ b/core/java/android/hardware/display/IRemoteDisplayAdapter.aidl @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +import android.hardware.display.WifiDisplayStatus; +import android.hardware.display.IDisplayDevice; + +/** + * @hide + */ +interface IRemoteDisplayAdapter { + // No permissions required. + oneway void scanRemoteDisplays(); + + // Requires CONFIGURE_WIFI_DISPLAY permission to connect to an unknown device. + // No permissions required to connect to a known device. + oneway void connectRemoteDisplay(String address); + + // No permissions required. + oneway void disconnectRemoteDisplay(); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + oneway void forgetRemoteDisplay(String address); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + oneway void renameRemoteDisplay(String address, String alias); + + // No permissions required. + /** + /* @hide + */ + WifiDisplayStatus getRemoteDisplayStatus(); + + oneway void registerDisplayDevice( + IDisplayDevice device, + String name, + int width, + int height, + float refreshRate, + int flags, + String address, + boolean hidden); + + oneway void unregisterDisplayDevice(IDisplayDevice device); +} diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index 2fd52b8f4e7a6..a18538ae01a9b 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -33,6 +33,7 @@ public final class WifiDisplay implements Parcelable { private final String mDeviceAddress; private final String mDeviceName; private final String mDeviceAlias; + private final boolean mHidden; public static final WifiDisplay[] EMPTY_ARRAY = new WifiDisplay[0]; @@ -50,6 +51,10 @@ public WifiDisplay[] newArray(int size) { }; public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias) { + this(deviceAddress, deviceName, deviceAlias, false); + } + + public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias, boolean hidden) { if (deviceAddress == null) { throw new IllegalArgumentException("deviceAddress must not be null"); } @@ -60,6 +65,7 @@ public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias) mDeviceAddress = deviceAddress; mDeviceName = deviceName; mDeviceAlias = deviceAlias; + mHidden = hidden; } /** @@ -95,6 +101,10 @@ public String getFriendlyDisplayName() { return mDeviceAlias != null ? mDeviceAlias : mDeviceName; } + public boolean isHidden() { + return mHidden; + } + @Override public boolean equals(Object o) { return o instanceof WifiDisplay && equals((WifiDisplay)o); diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java index 19c5f39eae4c8..0f7d9a088b2c4 100644 --- a/core/java/android/net/CaptivePortalTracker.java +++ b/core/java/android/net/CaptivePortalTracker.java @@ -157,10 +157,6 @@ public void detectCaptivePortal(NetworkInfo info) { } private class DefaultState extends State { - @Override - public void enter() { - setNotificationOff(); - } @Override public boolean processMessage(Message message) { @@ -186,6 +182,7 @@ public boolean processMessage(Message message) { private class NoActiveNetworkState extends State { @Override public void enter() { + setNotificationOff(); mNetworkInfo = null; } @@ -214,11 +211,6 @@ public boolean processMessage(Message message) { } private class ActiveNetworkState extends State { - @Override - public void enter() { - setNotificationOff(); - } - @Override public boolean processMessage(Message message) { NetworkInfo info; @@ -262,6 +254,8 @@ public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString()); switch (message.what) { case CMD_DELAYED_CAPTIVE_CHECK: + setNotificationOff(); + if (message.arg1 == mDelayedCheckToken) { InetAddress server = lookupHost(mServer); boolean captive = server != null && isCaptivePortal(server); @@ -340,8 +334,10 @@ private boolean isActiveNetwork(NetworkInfo info) { private void setNotificationOff() { try { - mConnService.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_NONE, + if (mNetworkInfo != null) { + mConnService.setProvisioningNotificationVisible(false, mNetworkInfo.getType(), null, null); + } } catch (RemoteException e) { log("setNotificationOff: " + e); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 02a649401ffdc..a5b396c42d3af 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.ActivityThread; import android.content.Context; import android.os.Binder; import android.os.Build.VERSION_CODES; @@ -849,7 +850,7 @@ public boolean getMobileDataEnabled() { */ public void setMobileDataEnabled(boolean enabled) { try { - mService.setMobileDataEnabled(enabled); + mService.setMobileDataEnabled(ActivityThread.currentPackageName(), enabled); } catch (RemoteException e) { } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index a17b4f5f95910..a24959fc5de76 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -72,7 +72,7 @@ interface IConnectivityManager boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress); boolean getMobileDataEnabled(); - void setMobileDataEnabled(boolean enabled); + void setMobileDataEnabled(String callingPackage, boolean enabled); /** Policy control over specific {@link NetworkStateTracker}. */ void setPolicyDataEnable(int networkType, boolean enabled); diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 409f4ef8cef2e..c93e6f0f8ac8c 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,12 +31,14 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.ServiceManager; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Slog; import com.android.internal.telephony.DctConstants; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.msim.ITelephonyMSim; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.AsyncChannel; @@ -57,6 +62,7 @@ public class MobileDataStateTracker implements NetworkStateTracker { private PhoneConstants.DataState mMobileDataState; private ITelephony mPhoneService; + private ITelephonyMSim mMSimPhoneService; private String mApnType; private NetworkInfo mNetworkInfo; @@ -190,8 +196,6 @@ private void updateLinkProperitesAndCapatilities(Intent intent) { private class MobileDataStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - // Assume this isn't a provisioning network. - mNetworkInfo.setIsConnectedToProvisioningNetwork(false); if (intent.getAction().equals(TelephonyIntents. ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) { String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); @@ -218,6 +222,8 @@ public void onReceive(Context context, Intent intent) { if (!TextUtils.equals(apnType, mApnType)) { return; } + // Assume this isn't a provisioning network. + mNetworkInfo.setIsConnectedToProvisioningNetwork(false); if (DBG) { log("Broadcast received: " + intent.getAction() + " apnType=" + apnType); } @@ -299,6 +305,8 @@ public void onReceive(Context context, Intent intent) { if (!TextUtils.equals(apnType, mApnType)) { return; } + // Assume this isn't a provisioning network. + mNetworkInfo.setIsConnectedToProvisioningNetwork(false); String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY); String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); if (DBG) { @@ -313,6 +321,13 @@ public void onReceive(Context context, Intent intent) { } private void getPhoneService(boolean forceRefresh) { + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + if (mMSimPhoneService == null || forceRefresh) { + mMSimPhoneService = ITelephonyMSim.Stub.asInterface( + ServiceManager.getService("phone_msim")); + } + return; + } if ((mPhoneService == null) || forceRefresh) { mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone")); } @@ -341,6 +356,7 @@ public String getTcpBufferSizesPropName() { networkTypeStr = "edge"; break; case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: networkTypeStr = "umts"; break; case TelephonyManager.NETWORK_TYPE_HSDPA: @@ -496,15 +512,33 @@ public boolean setRadio(boolean turnOn) { * RemoteException and need to re-reference the service. */ for (int retry = 0; retry < 2; retry++) { - if (mPhoneService == null) { - loge("Ignoring mobile radio request because could not acquire PhoneService"); - break; - } + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + if (mMSimPhoneService == null) { + loge("Ignoring mobile radio request because " + + "could not acquire MSim Phone Service"); + break; + } - try { - return mPhoneService.setRadio(turnOn); - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); + try { + boolean result = true; + for (int i = 0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + result = result && mMSimPhoneService.setRadio(turnOn, i); + } + return result; + } catch (RemoteException e) { + if (retry == 0) getPhoneService(true); + } + } else { + if (mPhoneService == null) { + loge("Ignoring mobile radio request because could not acquire PhoneService"); + break; + } + + try { + return mPhoneService.setRadio(turnOn); + } catch (RemoteException e) { + if (retry == 0) getPhoneService(true); + } } } @@ -635,19 +669,36 @@ private int setEnableApn(String apnType, boolean enable) { * RemoteException and need to re-reference the service. */ for (int retry = 0; retry < 2; retry++) { - if (mPhoneService == null) { - loge("Ignoring feature request because could not acquire PhoneService"); - break; - } + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + if (mMSimPhoneService == null) { + loge("Ignoring feature request because could not acquire MSim Phone Service"); + break; + } - try { - if (enable) { - return mPhoneService.enableApnType(apnType); - } else { - return mPhoneService.disableApnType(apnType); + try { + if (enable) { + return mMSimPhoneService.enableApnType(apnType); + } else { + return mMSimPhoneService.disableApnType(apnType); + } + } catch (RemoteException e) { + if (retry == 0) getPhoneService(true); + } + } else { + if (mPhoneService == null) { + loge("Ignoring feature request because could not acquire PhoneService"); + break; + } + + try { + if (enable) { + return mPhoneService.enableApnType(apnType); + } else { + return mPhoneService.disableApnType(apnType); + } + } catch (RemoteException e) { + if (retry == 0) getPhoneService(true); } - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); } } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 45524c82c9e27..293c8974d12f2 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -1,6 +1,10 @@ /* //device/java/android/android/os/INetworkManagementService.aidl ** ** Copyright 2007, The Android Open Source Project +** Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. +** +** Not a Contribution. Apache license notifications and license are +** retained for attribution purposes only. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -188,6 +192,16 @@ interface INetworkManagementService */ void disableNat(String internalInterface, String externalInterface); + /** + * Add an upstream IPv6 interface + */ + void addUpstreamV6Interface(String iface); + + /** + * Remove an upstream IPv6 interface + */ + void removeUpstreamV6Interface(String iface); + /** ** PPPD **/ @@ -367,4 +381,42 @@ interface INetworkManagementService * Determine whether the clatd (464xlat) service has been started */ boolean isClatdStarted(); + + /** + ** Policy Routing + **/ + + /** + * Replaces a prexisting identical route with the new metric specified. + * Adds a new route if none existed before. + */ + boolean addRouteWithMetric(String iface, int metric, in RouteInfo route); + + /** + * Replaces a source policy route for the given iface in a custom routing + * table denoted by routeId, if it already exists. + * Adds a new route if it did not exist. + */ + boolean replaceSrcRoute(String iface, in byte[] ip, in byte[] gateway, int routeId); + + /** + * Deletes a source policy route for the given route identifier and source + * address from a custom routing table denoted by routeId + */ + boolean delSrcRoute(in byte[] ip, int routeId); + + /** + * Set SAP Channel Range + */ + void setChannelRange(int startchannel, int endchannel, int band); + + /** + * Get SAP Current Operating Channel + */ + int getSapOperatingChannel(); + + /** + * Get SAP Auto Channel Selection + */ + int getSapAutoChannelSelection(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e9d553b3996b0..cad29bd8169b0 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -2938,6 +2941,24 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final String VOLBTN_MUSIC_CONTROLS = "volbtn_music_controls"; + /** + * Whether to wake the screen with the camera key half-press. + * @hide + */ + public static final String CAMERA_WAKE_SCREEN = "camera_wake_screen"; + + /** + * Whether or not to send device back to sleep if Camera button is released ("Peek") + * @hide + */ + public static final String CAMERA_SLEEP_ON_RELEASE = "camera_sleep_on_release"; + + /** + * Whether or not camera button music controls should be enabled to play/pause media tracks + * @hide + */ + public static final String CAMERA_MUSIC_CONTROLS = "camera_music_controls"; + /** * Whether or not to launch default music player when headset is connected * @hide @@ -2980,6 +3001,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final String QUIET_HOURS_HAPTIC = "quiet_hours_haptic"; + /** + * Whether to remove the system sounds during quiet hours. + * @hide + */ + public static final String QUIET_HOURS_SYSTEM = "quiet_hours_system"; + /** * Whether to remove the vibration from outgoing notifications during quiet hours. * @hide @@ -3135,21 +3162,20 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean public static final String HOME_UNLOCK_SCREEN = "home_unlock_screen"; /** - * Whether the lockscreen vibrate should be enabled. + * Whether to unlock the screen with the camera key. The value is boolean (1 or 0). * @hide */ - public static final String LOCKSCREEN_VIBRATE_ENABLED = "lockscreen.vibrate_enabled"; + public static final String CAMERA_UNLOCK_SCREEN = "camera_unlock_screen"; /** - * Show the pending notification counts as overlays on the status bar - * Whether to enable custom rebindings of the actions performed on - * certain key press events. + * Whether the lockscreen vibrate should be enabled. * @hide */ - public static final String HARDWARE_KEY_REBINDING = "hardware_key_rebinding"; + public static final String LOCKSCREEN_VIBRATE_ENABLED = "lockscreen.vibrate_enabled"; /** - * Action to perform when the home key is long-pressed. (Default is 2) + * Action to perform when the home key is long-pressed. + * (Default can be configured via config_longPressOnHomeBehavior) * 0 - Nothing * 1 - Menu * 2 - App-switch @@ -3162,7 +3188,8 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean public static final String KEY_HOME_LONG_PRESS_ACTION = "key_home_long_press_action"; /** - * Action to perform when the home key is double-tapped. (Default is 0) + * Action to perform when the home key is double-tapped. + * (Default can be configured via config_doubleTapOnHomeBehavior) * (See KEY_HOME_LONG_PRESS_ACTION for valid values) * @hide */ @@ -3205,41 +3232,26 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final String LOCKSCREEN_MAXIMIZE_WIDGETS = "lockscreen_maximize_widgets"; - /** - * Action to perform when the assistant (search) key is long-pressed. (Default is 4) - * (See KEY_HOME_LONG_PRESS_ACTION for valid values) - * @hide - */ - public static final String KEY_ASSIST_LONG_PRESS_ACTION = "key_assist_long_press_action"; - - /** - * Action to perform when the app switch key is pressed. (Default is 2) - * (See KEY_HOME_LONG_PRESS_ACTION for valid values) - * @hide - */ - public static final String KEY_APP_SWITCH_ACTION = "key_app_switch_action"; - - /** - * Action to perform when the app switch key is long-pressed. (Default is 0) - * (See KEY_HOME_LONG_PRESS_ACTION for valid values) - * @hide - */ - public static final String KEY_APP_SWITCH_LONG_PRESS_ACTION = "key_app_switch_long_press_action"; - - /** - * Action to perform when the Camera key is pressed. (Default is 0) - * (See KEY_HOME_LONG_PRESS_ACTION for valid values) - * @hide - */ - public static final String KEY_CAMERA_ACTION = "key_camera_action"; + /** + * Action to perform when the assistant (search) key is long-pressed. (Default is 4) + * (See KEY_HOME_LONG_PRESS_ACTION for valid values) + * @hide + */ + public static final String KEY_ASSIST_LONG_PRESS_ACTION = "key_assist_long_press_action"; - /** - * Action to perform when the Camera key is long-pressed. (Default is 6) - * (See KEY_HOME_LONG_PRESS_ACTION for valid values) - * @hide - */ - public static final String KEY_CAMERA_LONG_PRESS_ACTION = "key_camera_long_press_action"; + /** + * Action to perform when the app switch key is pressed. (Default is 2) + * (See KEY_HOME_LONG_PRESS_ACTION for valid values) + * @hide + */ + public static final String KEY_APP_SWITCH_ACTION = "key_app_switch_action"; + /** + * Action to perform when the app switch key is long-pressed. (Default is 0) + * (See KEY_HOME_LONG_PRESS_ACTION for valid values) + * @hide + */ + public static final String KEY_APP_SWITCH_LONG_PRESS_ACTION = "key_app_switch_long_press_action"; /** * Control the display of the action overflow button within app UI. @@ -3292,6 +3304,13 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final String VOLUME_KEYS_CONTROL_RING_STREAM = "volume_keys_control_ring_stream"; + /** + * Performance profile + * @see config_perf_profile_prop in frameworks/base/core/res/res/values/config.xml + * @hide + */ + public static final String PERFORMANCE_PROFILE = "performance_profile"; + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. @@ -3371,6 +3390,7 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean QUIET_HOURS_START, QUIET_HOURS_END, QUIET_HOURS_MUTE, + QUIET_HOURS_SYSTEM, QUIET_HOURS_STILL, QUIET_HOURS_DIM, SYSTEM_PROFILES_ENABLED, @@ -3768,7 +3788,6 @@ public static final class Secure extends NameValueTable { MOVED_TO_GLOBAL.add(Settings.Global.SET_GLOBAL_HTTP_PROXY); MOVED_TO_GLOBAL.add(Settings.Global.DEFAULT_DNS_SERVER); MOVED_TO_GLOBAL.add(Settings.Global.PREFERRED_NETWORK_MODE); - MOVED_TO_GLOBAL.add(Settings.Global.PREFERRED_CDMA_SUBSCRIPTION); } /** @hide */ @@ -6497,14 +6516,6 @@ public static final String getBluetoothInputDevicePriorityKey(String address) { public static final String PREFERRED_NETWORK_MODE = "preferred_network_mode"; - /** - * The cdma subscription 0 = Subscription from RUIM, when available - * 1 = Subscription from NV - * @hide - */ - public static final String PREFERRED_CDMA_SUBSCRIPTION = - "preferred_cdma_subscription"; - /** * Name of an application package to be debugged. */ @@ -6914,6 +6925,67 @@ public static float getFloat(ContentResolver cr, String name) public static boolean putFloat(ContentResolver cr, String name, float value) { return putString(cr, name, Float.toString(value)); } + + + /** + * Subscription to be used for voice call on a multi sim device. The supported values + * are 0 = SUB1, 1 = SUB2 and etc. + * @hide + */ + public static final String MULTI_SIM_VOICE_CALL_SUBSCRIPTION = "multi_sim_voice_call"; + + /** + * Used to provide option to user to select subscription during dial. + * The supported values are 0 = disable or 1 = enable prompt. + * @hide + */ + public static final String MULTI_SIM_VOICE_PROMPT = "multi_sim_voice_prompt"; + + /** + * Subscription to be used for data call on a multi sim device. The supported values + * are 0 = SUB1, 1 = SUB2 and etc. + * @hide + */ + public static final String MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call"; + + /** + * Subscription to be used for SMS on a multi sim device. The supported values + * are 0 = SUB1, 1 = SUB2 and etc. + * @hide + */ + public static final String MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms"; + + /** + * Used to provide option to user to select subscription during send SMS. + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt"; + + + + /** User preferred subscriptions setting. + * This holds the details of the user selected subscription from the card and + * the activation status. Each settings string have the coma separated values + * iccId,appType,appId,activationStatus,3gppIndex,3gpp2Index + * @hide + */ + public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1", + "user_preferred_sub2","user_preferred_sub3"}; + + /** + * Subscription to be used decide priority sub on a multi sim device. The supported values + * are 0 = SUB1, 1 = SUB2, 2 = SUB3. + * @hide + */ + public static final String MULTI_SIM_PRIORITY_SUBSCRIPTION = "multi_sim_priority"; + + /** + * To know the status of tune away. The supported values + * are false = disable, true = enable. + * @hide + */ + public static final String TUNE_AWAY_STATUS = "tune_away"; } /** diff --git a/core/java/android/service/gesture/IGestureService.aidl b/core/java/android/service/gesture/IGestureService.aidl new file mode 100644 index 0000000000000..1944d5075d2ee --- /dev/null +++ b/core/java/android/service/gesture/IGestureService.aidl @@ -0,0 +1,11 @@ +package android.service.gesture; + +import android.app.PendingIntent; + +/** @hide */ +interface IGestureService { + + void setOnLongPressPendingIntent(in PendingIntent pendingIntent); + void setOnDoubleClickPendingIntent(in PendingIntent pendingIntent); + +} diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 2a761c1b380f2..f6367107e34b4 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -219,6 +219,18 @@ public final class InputDevice implements Parcelable { */ public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE; + /** + * The input source is a touch device whose motions should be interpreted as gestures. + * + * For example, an upward swipe should be treated the same as a swipe of the touchscreen. + * The same should apply for left, right, down swipes. Complex gestures may also be input. + * + * @see #SOURCE_CLASS_NONE + * + * @hide + */ + public static final int SOURCE_GESTURE_SENSOR = 0x00400000 | SOURCE_CLASS_NONE; + /** * The input source is a joystick. * (It may also be a {@link #SOURCE_GAMEPAD}). @@ -787,6 +799,7 @@ public String toString() { appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad"); appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK, "joystick"); appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad"); + appendSourceDescriptionIfApplicable(description, SOURCE_GESTURE_SENSOR, "gesture"); description.append(" )\n"); final int numAxes = mMotionRanges.size(); diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index fa73f8f2dd51e..8ff5c354aa6dd 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -34,6 +34,7 @@ interface IAppOpsService { List getOpsForPackage(int uid, String packageName, in int[] ops); void setMode(int code, int uid, String packageName, int mode); void resetAllModes(); + void resetCounters(); // Privacy guard methods boolean getPrivacyGuardSettingForPackage(int uid, String packageName); diff --git a/core/java/com/android/internal/util/cm/QSConstants.java b/core/java/com/android/internal/util/cm/QSConstants.java index d6865b1c5a59d..27cdaedc361bb 100644 --- a/core/java/com/android/internal/util/cm/QSConstants.java +++ b/core/java/com/android/internal/util/cm/QSConstants.java @@ -24,6 +24,7 @@ public class QSConstants { public static final String TILE_LTE = "toggleLte"; public static final String TILE_WIMAX = "toggleWimax"; public static final String TILE_PROFILE = "toggleProfile"; + public static final String TILE_PERFORMANCE_PROFILE = "togglePerformanceProfile"; public static final String TILE_NFC = "toggleNfc"; public static final String TILE_USBTETHER = "toggleUsbTether"; public static final String TILE_QUIETHOURS = "toggleQuietHours"; @@ -41,6 +42,8 @@ public class QSConstants { TILES_DEFAULT.add(TILE_SETTINGS); TILES_DEFAULT.add(TILE_WIFI); TILES_DEFAULT.add(TILE_MOBILEDATA); + TILES_DEFAULT.add(TILE_GPS); + TILES_DEFAULT.add(TILE_TORCH); TILES_DEFAULT.add(TILE_BATTERY); TILES_DEFAULT.add(TILE_AIRPLANE); TILES_DEFAULT.add(TILE_BLUETOOTH); diff --git a/core/java/com/android/internal/util/cm/QSUtils.java b/core/java/com/android/internal/util/cm/QSUtils.java index 6c8793ce4211f..f205a8a85c9f9 100644 --- a/core/java/com/android/internal/util/cm/QSUtils.java +++ b/core/java/com/android/internal/util/cm/QSUtils.java @@ -1,8 +1,10 @@ package com.android.internal.util.cm; +import android.R; import android.bluetooth.BluetoothAdapter; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.Camera; import android.hardware.display.DisplayManager; @@ -12,6 +14,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; +import android.text.TextUtils; import com.android.internal.telephony.PhoneConstants; @@ -44,6 +47,13 @@ public static boolean systemProfilesEnabled(ContentResolver resolver) { return (Settings.System.getInt(resolver, Settings.System.SYSTEM_PROFILES_ENABLED, 1) == 1); } + public static boolean deviceSupportsPerformanceProfiles(Context ctx) { + Resources res = ctx.getResources(); + String perfProfileProp = res.getString( + com.android.internal.R.string.config_perf_profile_prop); + return !TextUtils.isEmpty(perfProfileProp); + } + public static boolean expandedDesktopEnabled(ContentResolver resolver) { return (Settings.System.getIntForUser(resolver, Settings.System.EXPANDED_DESKTOP_STYLE, 0, UserHandle.USER_CURRENT_OR_SELF) != 0); @@ -67,6 +77,14 @@ public static boolean deviceSupportsCamera() { return Camera.getNumberOfCameras() > 0; } + public static boolean deviceSupportsGps(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); + } + + public static boolean deviceSupportsTorch(Context context) { + return context.getResources().getBoolean(com.android.internal.R.bool.config_enableTorch); + } + public static boolean adbEnabled(ContentResolver resolver) { return (Settings.Global.getInt(resolver, Settings.Global.ADB_ENABLED, 0)) == 1; } diff --git a/core/java/com/android/internal/util/cm/QuietHoursUtils.java b/core/java/com/android/internal/util/cm/QuietHoursUtils.java new file mode 100644 index 0000000000000..bfb2085343d4b --- /dev/null +++ b/core/java/com/android/internal/util/cm/QuietHoursUtils.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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.util.cm; + +import android.content.Context; +import android.provider.Settings; + +import java.util.Calendar; + +public class QuietHoursUtils { + public static boolean inQuietHours(Context context, String option) { + boolean quietHoursEnabled = Settings.System.getInt(context.getContentResolver(), + Settings.System.QUIET_HOURS_ENABLED, 0) != 0; + int quietHoursStart = Settings.System.getInt(context.getContentResolver(), + Settings.System.QUIET_HOURS_START, 0); + int quietHoursEnd = Settings.System.getInt(context.getContentResolver(), + Settings.System.QUIET_HOURS_END, 0); + boolean quietHoursOption = Settings.System.getInt(context.getContentResolver(), + option, 0) != 0; + if (quietHoursEnabled && quietHoursOption && (quietHoursStart != quietHoursEnd)) { + // Get the date in "quiet hours" format. + Calendar calendar = Calendar.getInstance(); + int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + + calendar.get(Calendar.MINUTE); + if (quietHoursEnd < quietHoursStart) { + // Starts at night, ends in the morning. + return (minutes > quietHoursStart) || (minutes < quietHoursEnd); + } else { + return (minutes > quietHoursStart) && (minutes < quietHoursEnd); + } + } + return false; + } +} diff --git a/core/java/org/codeaurora/camera/QCFace.java b/core/java/org/codeaurora/camera/QCFace.java new file mode 100644 index 0000000000000..9198c286ce863 --- /dev/null +++ b/core/java/org/codeaurora/camera/QCFace.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 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 org.codeaurora.camera; + +import android.os.Bundle; + +/** + * {@hide} Information about a Qaulcomm face identified through camera face + * detection. + * + *

+ * When face detection is used with a camera, the {@link FaceDetectionListener} + * returns a list of face objects for use in focusing and metering. + *

+ * + * @see FaceDetectionListener + */ +public class QCFace extends android.hardware.Camera.Face { + public QCFace() { + super(); + } + + private int smileDegree = 0; + private int smileScore = 0; + private int blinkDetected = 0; + private int faceRecognized = 0; + private int gazeAngle = 0; + private int updownDir = 0; + private int leftrightDir = 0; + private int rollDir = 0; + private int leyeBlink = 0; + private int reyeBlink = 0; + private int leftrightGaze = 0; + private int topbottomGaze = 0; + + /** + * The smilie degree for the detection of the face. + * + * @see #startFaceDetection() + */ + public int getSmileDegree() { + return smileDegree; + } + + /** + * The smilie score for the detection of the face. + * + * @see #startFaceDetection() + */ + public int getSmileScore() { + return smileScore; + } + + /** + * The smilie degree for the detection of the face. + * + * @see #startFaceDetection() + */ + public int getBlinkDetected() { + return blinkDetected; + } + + /** + * If face is recognized. + * + * @see #startFaceDetection() + */ + public int getFaceRecognized() { + return faceRecognized; + } + + /** + * The gaze angle for the detected face. + * + * @see #startFaceDetection() + */ + public int getGazeAngle() { + return gazeAngle; + } + + /** + * The up down direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getUpDownDirection() { + return updownDir; + } + + /** + * The left right direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getLeftRightDirection() { + return leftrightDir; + } + + /** + * The roll direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getRollDirection() { + return rollDir; + } + + /** + * The degree of left eye blink for the detected face. + * + * @see #startFaceDetection() + */ + public int getLeftEyeBlinkDegree() { + return leyeBlink; + } + + /** + * The degree of right eye blink for the detected face. + * + * @see #startFaceDetection() + */ + public int getRightEyeBlinkDegree() { + return reyeBlink; + } + + /** + * The gaze degree of left-right direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getLeftRightGazeDegree() { + return leftrightGaze; + } + + /** + * The gaze degree of up-down direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getTopBottomGazeDegree() { + return topbottomGaze; + } + + private static final String BUNDLE_KEY_SMILE_SCORE = "smileScore"; + private static final String BUNDLE_KEY_SMILE_VALUE = "smileValue"; + private static final String BUNDLE_KEY_BLINK_DETECTED = "blinkDetected"; + private static final String BUNDLE_KEY_LEFT_EYE_CLOSED_VALUE = "leftEyeClosedValue"; + private static final String BUNDLE_KEY_RIGHT_EYE_CLOSED_VALUE = "rightEyeClosedValue"; + private static final String BUNDLE_KEY_FACE_PITCH_DEGREE = "facePitchDegree"; + private static final String BUNDLE_KEY_FACE_YAW_DEGREE = "faceYawDegree"; + private static final String BUNDLE_KEY_FACE_ROLL_DEGREE = "faceRollDegree"; + private static final String BUNDLE_KEY_GAZE_UP_DOWN_DEGREE = "gazeUpDownDegree"; + private static final String BUNDLE_KEY_GAZE_LEFT_RIGHT_DEGREE = "gazeLeftRightDegree"; + private static final String BUNDLE_KEY_FACE_RECOGNIZED = "faceRecognized"; + + public Bundle getQCFaceInfo() { + Bundle faceInfo = new Bundle(); + faceInfo.putInt(BUNDLE_KEY_SMILE_VALUE, this.smileDegree); + + faceInfo.putInt(BUNDLE_KEY_LEFT_EYE_CLOSED_VALUE, this.leyeBlink); + faceInfo.putInt(BUNDLE_KEY_RIGHT_EYE_CLOSED_VALUE, this.reyeBlink); + + faceInfo.putInt(BUNDLE_KEY_FACE_PITCH_DEGREE, this.updownDir); + faceInfo.putInt(BUNDLE_KEY_FACE_YAW_DEGREE, this.leftrightDir); + faceInfo.putInt(BUNDLE_KEY_FACE_ROLL_DEGREE, this.rollDir); + faceInfo.putInt(BUNDLE_KEY_GAZE_UP_DOWN_DEGREE, this.topbottomGaze); + faceInfo.putInt(BUNDLE_KEY_GAZE_LEFT_RIGHT_DEGREE, this.leftrightGaze); + + faceInfo.putInt(BUNDLE_KEY_BLINK_DETECTED, this.blinkDetected); + faceInfo.putInt(BUNDLE_KEY_SMILE_SCORE, this.smileScore); + faceInfo.putInt(BUNDLE_KEY_FACE_RECOGNIZED, this.faceRecognized); + + return faceInfo; + } + +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index b07b3c8b9aa9e..53c92b35e6231 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -250,6 +250,10 @@ ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK endif +ifeq ($(BOARD_FIX_FACE_DETECTION_SCORE),true) + LOCAL_CFLAGS += -DFIX_FACE_DETECTION_SCORE +endif + LOCAL_MODULE:= libandroid_runtime include $(BUILD_SHARED_LIBRARY) diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index b218bcddb409c..35d56cf6b1e38 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -158,6 +158,11 @@ static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, // for now we don't allow shareable with java inputstreams SkMemoryStream *mStream = buildSkMemoryStream(stream); largeBitmap = doBuildTileIndex(env, mStream); + // release the resources when decoding fails + if (largeBitmap == NULL) { + delete(mStream); + mStream = NULL; + } stream->unref(); } return largeBitmap; diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 450b785cdf2f8..3525d13bcd5d2 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -49,6 +49,27 @@ struct fields_t { jmethodID post_event; jmethodID rect_constructor; jmethodID face_constructor; + jfieldID face_id; + jfieldID face_leftEye; + jfieldID face_rightEye; + jfieldID face_mouth; +#ifdef QCOM_HARDWARE + jfieldID face_sm_degree; + jfieldID face_sm_score; + jfieldID face_blink_detected; + jfieldID face_gaze_angle; + jfieldID face_updown_dir; + jfieldID face_leftright_dir; + jfieldID face_roll_dir; + jfieldID face_leye_blink; + jfieldID face_reye_blink; + jfieldID face_left_right_gaze; + jfieldID face_top_bottom_gaze; +#endif + jfieldID face_recognised; + jfieldID point_x; + jfieldID point_y; + jmethodID point_constructor; }; static fields_t fields; @@ -82,6 +103,8 @@ class JNICameraContext: public CameraListener sp mCamera; // strong reference to native object jclass mFaceClass; // strong reference to Face class jclass mRectClass; // strong reference to Rect class + jclass mPointClass; // strong reference to Point class + bool mIsQcFace; Mutex mLock; /* @@ -132,12 +155,22 @@ JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, mCameraJClass = (jclass)env->NewGlobalRef(clazz); mCamera = camera; - jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); - mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + jclass qcfaceClazz = env->FindClass("org/codeaurora/camera/QCFace"); + if (NULL != qcfaceClazz) { + mFaceClass = (jclass) env->NewGlobalRef(qcfaceClazz); + mIsQcFace = true; + } else { + jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); + mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + mIsQcFace = false; + } jclass rectClazz = env->FindClass("android/graphics/Rect"); mRectClass = (jclass) env->NewGlobalRef(rectClazz); + jclass pointClazz = env->FindClass("android/graphics/Point"); + mPointClass = (jclass) env->NewGlobalRef(pointClazz); + mManualBufferMode = false; mManualCameraCallbackSet = false; } @@ -164,6 +197,10 @@ void JNICameraContext::release() env->DeleteGlobalRef(mRectClass); mRectClass = NULL; } + if (mPointClass != NULL) { + env->DeleteGlobalRef(mPointClass); + mPointClass = NULL; + } clearCallbackBuffers_l(env); mCamera.clear(); } @@ -346,9 +383,50 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(rect, fields.rect_top, metadata->faces[i].rect[1]); env->SetIntField(rect, fields.rect_right, metadata->faces[i].rect[2]); env->SetIntField(rect, fields.rect_bottom, metadata->faces[i].rect[3]); - env->SetObjectField(face, fields.face_rect, rect); +#ifdef FIX_FACE_DETECTION_SCORE + env->SetIntField(face, fields.face_score, (metadata->faces[i].score ? 0 : 100)); +#else env->SetIntField(face, fields.face_score, metadata->faces[i].score); +#endif + +#ifdef QCOM_HARDWARE + jobject point1 = env->NewObject(mPointClass, fields.point_constructor); + env->SetIntField(point1, fields.point_x, metadata->faces[i].left_eye[0]); + env->SetIntField(point1, fields.point_y, metadata->faces[i].left_eye[1]); + env->SetObjectField(face, fields.face_leftEye, point1); + + jobject point2 = env->NewObject(mPointClass, fields.point_constructor); + env->SetIntField(point2, fields.point_x, metadata->faces[i].right_eye[0]); + env->SetIntField(point2, fields.point_y, metadata->faces[i].right_eye[1]); + env->SetObjectField(face, fields.face_rightEye, point2); + + jobject point3 = env->NewObject(mPointClass, fields.point_constructor); + env->SetIntField(point3, fields.point_x, metadata->faces[i].mouth[0]); + env->SetIntField(point3, fields.point_y, metadata->faces[i].mouth[1]); + env->SetObjectField(face, fields.face_mouth, point3); + + env->SetIntField(face, fields.face_id, metadata->faces[i].id); + + if (mIsQcFace) { + env->SetIntField(face, fields.face_sm_degree, metadata->faces[i].smile_degree); + env->SetIntField(face, fields.face_sm_score, metadata->faces[i].smile_score); + env->SetIntField(face, fields.face_blink_detected, metadata->faces[i].blink_detected); + env->SetIntField(face, fields.face_recognised, metadata->faces[i].face_recognised); + env->SetIntField(face, fields.face_gaze_angle, metadata->faces[i].gaze_angle); + env->SetIntField(face, fields.face_updown_dir, metadata->faces[i].updown_dir); + env->SetIntField(face, fields.face_leftright_dir, metadata->faces[i].leftright_dir); + env->SetIntField(face, fields.face_roll_dir, metadata->faces[i].roll_dir); + env->SetIntField(face, fields.face_leye_blink, metadata->faces[i].leye_blink); + env->SetIntField(face, fields.face_reye_blink, metadata->faces[i].reye_blink); + env->SetIntField(face, fields.face_left_right_gaze, metadata->faces[i].left_right_gaze); + env->SetIntField(face, fields.face_top_bottom_gaze, metadata->faces[i].top_bottom_gaze); + } + env->DeleteLocalRef(point1); + env->DeleteLocalRef(point2); + env->DeleteLocalRef(point3); + +#endif env->DeleteLocalRef(face); env->DeleteLocalRef(rect); @@ -385,6 +463,25 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM } } +static void android_hardware_Camera_setLongshot(JNIEnv *env, jobject thiz, jboolean enable) +{ + ALOGV("setLongshot"); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if ( enable ) { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_ON, 0, 0); + } else { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_OFF, 0, 0); + } + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "enabling longshot mode failed"); + } +} + static void android_hardware_Camera_sendHistogramData(JNIEnv *env, jobject thiz) { ALOGV("sendHistogramData" ); @@ -666,6 +763,24 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t context->setCallbackMode(env, installed, manualBuffer); } +static void android_hardware_Camera_setMetadataCb(JNIEnv *env, jobject thiz, jboolean mode) +{ + ALOGV("setMetadataCb: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_METADATA_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_METADATA_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set metadata mode failed"); + } +} + static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, int msgType) { ALOGV("addCallbackBuffer: 0x%x", msgType); @@ -958,9 +1073,15 @@ static JNINativeMethod camMethods[] = { { "native_setHistogramMode", "(Z)V", (void *)android_hardware_Camera_setHistogramMode }, + { "native_setMetadataCb", + "(Z)V", + (void *)android_hardware_Camera_setMetadataCb }, { "native_sendHistogramData", "()V", (void *)android_hardware_Camera_sendHistogramData }, + { "native_setLongshot", + "(Z)V", + (void *)android_hardware_Camera_setLongshot }, { "native_setParameters", "(Ljava/lang/String;)V", (void *)android_hardware_Camera_setParameters }, @@ -1040,12 +1161,50 @@ int register_android_hardware_Camera(JNIEnv *env) { "android/hardware/Camera$CameraInfo", "orientation", "I", &fields.orientation }, { "android/hardware/Camera$CameraInfo", "canDisableShutterSound", "Z", &fields.canDisableShutterSound }, - { "android/hardware/Camera$Face", "rect", "Landroid/graphics/Rect;", &fields.face_rect }, - { "android/hardware/Camera$Face", "score", "I", &fields.face_score }, { "android/graphics/Rect", "left", "I", &fields.rect_left }, { "android/graphics/Rect", "top", "I", &fields.rect_top }, { "android/graphics/Rect", "right", "I", &fields.rect_right }, { "android/graphics/Rect", "bottom", "I", &fields.rect_bottom }, + { "android/graphics/Point", "x", "I", &fields.point_x }, + { "android/graphics/Point", "y", "I", &fields.point_y }, + }; + + field facefields_to_find[] = { + { "android/hardware/Camera$Face", "rect", "Landroid/graphics/Rect;", &fields.face_rect }, + { "android/hardware/Camera$Face", "score", "I", &fields.face_score }, + { "android/hardware/Camera$Face", "id", "I", &fields.face_id }, + { "android/hardware/Camera$Face", "leftEye", "Landroid/graphics/Point;", &fields.face_leftEye }, + { "android/hardware/Camera$Face", "rightEye", "Landroid/graphics/Point;", &fields.face_rightEye }, + { "android/hardware/Camera$Face", "mouth", "Landroid/graphics/Point;", &fields.face_mouth }, +#ifdef QCOM_HARDWARE + { "android/hardware/Camera$Face", "smileDegree", "I", &fields.face_sm_degree }, + { "android/hardware/Camera$Face", "smileScore", "I", &fields.face_sm_score }, + { "android/hardware/Camera$Face", "blinkDetected", "I", &fields.face_blink_detected }, +#endif + { "android/hardware/Camera$Face", "faceRecognised", "I", &fields.face_recognised }, + }; + + field qcfacefields_to_find[] = { + { "org/codeaurora/camera/QCFace", "rect", "Landroid/graphics/Rect;", &fields.face_rect }, + { "org/codeaurora/camera/QCFace", "score", "I", &fields.face_score }, + { "org/codeaurora/camera/QCFace", "id", "I", &fields.face_id }, +#ifdef QCOM_HARDWARE + { "org/codeaurora/camera/QCFace", "leftEye", "Landroid/graphics/Point;", &fields.face_leftEye }, + { "org/codeaurora/camera/QCFace", "rightEye", "Landroid/graphics/Point;", &fields.face_rightEye }, + { "org/codeaurora/camera/QCFace", "mouth", "Landroid/graphics/Point;", &fields.face_mouth }, + { "org/codeaurora/camera/QCFace", "smileDegree", "I", &fields.face_sm_degree }, + { "org/codeaurora/camera/QCFace", "smileScore", "I", &fields.face_sm_score }, + { "org/codeaurora/camera/QCFace", "blinkDetected", "I", &fields.face_blink_detected }, + { "org/codeaurora/camera/QCFace", "faceRecognized", "I", &fields.face_recognised }, + { "org/codeaurora/camera/QCFace", "gazeAngle", "I", &fields.face_gaze_angle }, + { "org/codeaurora/camera/QCFace", "updownDir", "I", &fields.face_updown_dir }, + { "org/codeaurora/camera/QCFace", "leftrightDir", "I", &fields.face_leftright_dir }, + { "org/codeaurora/camera/QCFace", "rollDir", "I", &fields.face_roll_dir }, + { "org/codeaurora/camera/QCFace", "leyeBlink", "I", &fields.face_leye_blink }, + { "org/codeaurora/camera/QCFace", "reyeBlink", "I", &fields.face_reye_blink }, + { "org/codeaurora/camera/QCFace", "leftrightGaze", "I", &fields.face_left_right_gaze }, + { "org/codeaurora/camera/QCFace", "topbottomGaze", "I", &fields.face_top_bottom_gaze }, +#endif }; if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) @@ -1066,13 +1225,28 @@ int register_android_hardware_Camera(JNIEnv *env) return -1; } + clazz = env->FindClass("android/graphics/Point"); + fields.point_constructor = env->GetMethodID(clazz, "", "()V"); + if (fields.point_constructor == NULL) { + ALOGE("Can't find android/graphics/Point.Point()"); + return -1; + } + + clazz = env->FindClass("org/codeaurora/camera/QCFace"); + if (NULL != clazz) { + fields.face_constructor = env->GetMethodID(clazz, "", "()V"); + if (find_fields(env, qcfacefields_to_find, NELEM(qcfacefields_to_find)) < 0) + return -1; + } else { clazz = env->FindClass("android/hardware/Camera$Face"); fields.face_constructor = env->GetMethodID(clazz, "", "()V"); if (fields.face_constructor == NULL) { ALOGE("Can't find android/hardware/Camera$Face.Face()"); return -1; + } + if (find_fields(env, facefields_to_find, NELEM(fields_to_find)) < 0) + return -1; } - // Register native functions return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera", camMethods, NELEM(camMethods)); diff --git a/core/res/res/drawable-hdpi/sym_keyboard_cancel.png b/core/res/res/drawable-hdpi/sym_keyboard_cancel.png new file mode 100644 index 0000000000000..70258d5c7a7bb Binary files /dev/null and b/core/res/res/drawable-hdpi/sym_keyboard_cancel.png differ diff --git a/core/res/res/drawable-ldpi/sym_keyboard_cancel.png b/core/res/res/drawable-ldpi/sym_keyboard_cancel.png new file mode 100755 index 0000000000000..70258d5c7a7bb Binary files /dev/null and b/core/res/res/drawable-ldpi/sym_keyboard_cancel.png differ diff --git a/core/res/res/drawable-mdpi/sym_keyboard_cancel.png b/core/res/res/drawable-mdpi/sym_keyboard_cancel.png new file mode 100644 index 0000000000000..70258d5c7a7bb Binary files /dev/null and b/core/res/res/drawable-mdpi/sym_keyboard_cancel.png differ diff --git a/core/res/res/drawable-xhdpi/sym_keyboard_cancel.png b/core/res/res/drawable-xhdpi/sym_keyboard_cancel.png new file mode 100644 index 0000000000000..70258d5c7a7bb Binary files /dev/null and b/core/res/res/drawable-xhdpi/sym_keyboard_cancel.png differ diff --git a/core/res/res/layout/keyguard_emergency_carrier_area.xml b/core/res/res/layout/keyguard_emergency_carrier_area.xml index b8a7654c8dce6..8a9f5dc842a18 100644 --- a/core/res/res/layout/keyguard_emergency_carrier_area.xml +++ b/core/res/res/layout/keyguard_emergency_carrier_area.xml @@ -2,6 +2,8 @@ + + + + + + + + + diff --git a/core/res/res/layout/msim_keyguard_sim_pin_view.xml b/core/res/res/layout/msim_keyguard_sim_pin_view.xml new file mode 100644 index 0000000000000..11e95b9c0cfee --- /dev/null +++ b/core/res/res/layout/msim_keyguard_sim_pin_view.xml @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/layout/msim_keyguard_sim_puk_view.xml b/core/res/res/layout/msim_keyguard_sim_puk_view.xml new file mode 100644 index 0000000000000..213e6bf8ea704 --- /dev/null +++ b/core/res/res/layout/msim_keyguard_sim_puk_view.xml @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/layout/permission_confirmation_dialog.xml b/core/res/res/layout/permission_confirmation_dialog.xml new file mode 100644 index 0000000000000..8a936e86bcf12 --- /dev/null +++ b/core/res/res/layout/permission_confirmation_dialog.xml @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml index 5523807d65480..cd921ef611584 100644 --- a/core/res/res/layout/transient_notification.xml +++ b/core/res/res/layout/transient_notification.xml @@ -19,6 +19,7 @@ --> Laat die program toe om die FM-ontvanger te beheer.
Beheer FM-sender Laat die program toe om die FM-sender te beheer. + Stuur skyn-SMS boodskappe + Laat die program skyn-SMS boodskappe stuur. Dit laat \'n program toe om SMS te stuur na betroubare programme. Kwaadwillige programme kan boodskappe voortdurend stuur, dan dit sal die kennisgewings stelsel blokkeer en die gebruiker ontwrig. %d%% ontlaai + Geen verwyderbare media beskikbaar nie\u2026 Dok SD-kaart Program beëindig Herlaai @@ -60,7 +63,32 @@ %1$s het nie toegang tot persoonlike data nie Profiele Geen - onderskep uitgaande SMS + Onderskep uitgaande SMS Laat \'n program toe om uitgaande SMS boodskappe te onderskep. Kwaadwillige programme kan die reg misbruik om uitgaande SMS boodskappe te verhoed. - + Kanselleer program kennisgewings + Laat die program toe om kennisgewings geskep deur ander programme te kanselleer. + Dwing SELinux af + Wissel SELinux beleid: Dwing of permissiewe mode. + Dwing MMAC + Wissel MMAC beleid: Dwing of permissiewe mode. + Onsuksesvol. Aktiveer SIM/RUIM grendel. + Verkeerde PUK kode! + PIN bywerking het misluk! + PUK bywerking het misluk! + Kode korek! + \nProbeerslae oor : + Ongeldige kaart. + SIM kaart is ongeldig. + SIM/RUIM is Perso-gesluit + USSD-versoek is verander na Skakel-versoek. + USSD-versoek is verander na SS-versoek. + USSD-versoek is verander na nuwe USSD versoek. + SS-versoek is verander na Skakel-versoek. + SS-versoek is verander na USSD-versoek. + SS-versoek is verander na nuwe SS-versoek. + SUB:%d : %s. + %s, %s + Bespaar krag + Gebalanseerd + Werkverrigting diff --git a/core/res/res/values-ca/cm_arrays.xml b/core/res/res/values-ca/cm_arrays.xml new file mode 100644 index 0000000000000..7eb1f2848200e --- /dev/null +++ b/core/res/res/values-ca/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + + Intentant accedir a l\'ubicació + Intentant accedir a l\'ubicació + Intentant accedir a l\'ubicació + Intentant utilitzar la vibració + Intentant llegir els contactes + Intentant modificar els contactes + Intentant llegir el registre de trucades + Intentant modificar el registre de trucades + Intentant llegir el calendari + Intentant modificar el calendari + Intentant accedir a l\'ubicació + Intentant publicar una notificació + Intentant accedir a l\'ubicació + Intentant fer una trucada de telèfon + Intentant llegir SMS/MMS + Intentant escriure/modificar SMS/MMS + Intentant rebre SMS/MMS + Intentant rebre SMS/MMS + Intentant rebre SMS/MMS + Intentant rebre SMS/MMS + Intentant enviar SMS/MMS + Intentant llegir SMS/MMS + Intentant escriure/modificar SMS/MMS + Intentant modificar ajustaments + Intentant dibuixar a sobre + Intentant accedir a les notificacions + Intentant accedir Càmera + Intentant enregistrar audio + Intentant reproduir audio + Intentant llegir el portapapers + Intentant modificar el portapapers + Intentant activar/desactivar Wifi + Intentant activar/desactivar bluetooth + Intentant activar/desactivar dades mòbils + Intentant programar despertar el dispositiu + + diff --git a/core/res/res/values-ca/cm_strings.xml b/core/res/res/values-ca/cm_strings.xml index 73b7ef3241f8c..666b414be161d 100644 --- a/core/res/res/values-ca/cm_strings.xml +++ b/core/res/res/values-ca/cm_strings.xml @@ -124,4 +124,24 @@ Commuta la política SELinux entre el mode imposat o permissiu. Imposa MMAC Commuta la política MMAC entre el mode imposat o permissiu. + Ha fallat. Activa el bloqueig SIM/RUIM. + Codi PUK incorrecte! + El PIN ha fallat! + El PUK ha fallat! + Codi Acceptat! + \nIntents restants : + Targeta invàlida. + La targeta SIM és invàlida. + La SIM/RUIM té un bloqueig personalitzat + La petició USSD està modificada a una nova petició DIAL. + La petició USSD està modificada a una nova petició SS. + La petició USSD està modificada a una nova petició USSD. + La petició SS està modificada a una nova petició DIAL. + La petició SS està modificada a una nova petició USSD. + La petició SS està modificada a una nova petició SS. + SUB:%d : %s. + %s, %s + Estalvi d\'energia + Equilibrat + Rendiment diff --git a/core/res/res/values-cs/cm_arrays.xml b/core/res/res/values-cs/cm_arrays.xml new file mode 100644 index 0000000000000..b7a90183acad6 --- /dev/null +++ b/core/res/res/values-cs/cm_arrays.xml @@ -0,0 +1,54 @@ + + + + + Pokus o získání pozice + Pokus o získání pozice + Pokus o získání pozice + Pokus o rying to use vibrate + Pokus o čtení kontaktů + Pokus o změnu kontaktů + Pokus o čtení seznamu hovorů + Pokus o změnu seznamu hovorů + Pokus o čtení kalendáře + Pokus o změnu kalendáře + Pokus o získání pozice + Pokus o odeslání upozornění + Pokus o získání pozice + Pokus o vytvoření telefonního hovoru + Pokus o čtení SMS/MMS + Pokus o uložení nebo změnu SMS/MMS + Pokus o přijmutí SMS/MMS + Pokus o přijmutí SMS/MMS + Pokus o přijmutí SMS/MMS + Pokus o přijmutí SMS/MMS + Pokus o odeslání SMS/MMS + Pokus o o čtení SMS/MMS + Pokus o uložení nebo změnu SMS/MMS + Pokus o změnu nastavení + Pokus o vykreslování do nejvyšší vrstvy + Pokus o přístup k upozorněním + Pokus o přístup k fotoaparátu + Pokus o nahrávání zvuku + Pokus o přehrávání zvuku + Pokus o načtení schránky + Pokus o změnu schránky + Pokus o přepnutí Wifi + Pokus o přepnutí Bluetooth + Pokus o přepnutí mobilních dat + Pokus o naplánování probuzení + + \ No newline at end of file diff --git a/core/res/res/values-cs/cm_strings.xml b/core/res/res/values-cs/cm_strings.xml index b3775b65f7559..7f7c020df8a57 100644 --- a/core/res/res/values-cs/cm_strings.xml +++ b/core/res/res/values-cs/cm_strings.xml @@ -14,7 +14,23 @@ limitations under the License. --> + Neúspěšné. Povolte zámek SIM/RUIM. + Nesprávný kód PUK! + Akce PIN se nezdařila! + Akce PUK se nezdařila! + Kód přijat! + \nZbývá pokusů: + Neplatná karta. + Karta SIM je neplatná. + Karta SIM/RUIM je zamčená Dotykem nastavit + Není dostupné žádné vyměnitelné médium\u2026 + Požadavek USSD je změněn na požadavek DIAL. + Požadavek USSD je změněn na požadavek SS. + Požadavek USSD je změněn na nový požadavek USSD. + Požadavek SS je změněn na požadavek DIAL. + Požadavek SS je změněn na požadavek USSD. + Požadavek SS je změněn na nový požadavek SS. Výchozí Práce @@ -47,7 +63,6 @@ Umožňuje zasílat SMS zprávy důvěryhodným aplikacím. Škodlivé aplikace mohou průběžně zasílat veliké množství zpráv a tím blokovat systém upozorňování a obtěžovat uživatele.
Vybíjení - %d%% - Není dostupné žádné vyměnitelné médium\u2026 SD karta doku Aplikace ukončena Restartovat @@ -74,4 +89,8 @@ Přepnutí politiky SELinux na vynucující (enforcing) nebo tolerantní (permissive). Vynucení MMAC Přepnutí politiky MMAC na vynucující (enforcing) nebo tolerantní (permissive). + + Úsporný + Vyvážený + Výkonný diff --git a/core/res/res/values-da/cm_arrays.xml b/core/res/res/values-da/cm_arrays.xml new file mode 100644 index 0000000000000..accc87547fa1d --- /dev/null +++ b/core/res/res/values-da/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + + Forsøger at få adgang til placering + Forsøger at få adgang til placering + Forsøger at få adgang til placering + Forsøger at bruge vibration + Forsøger at læse kontaktpersoner + Forsøger at ændre kontaktpersoner + Forsøger at læse opkaldsloggen + Forsøger at ændre opkaldsloggen + Forsøger at læse kalenderen + Forsøger at ændre kalenderen + Forsøger at få adgang til placering + Forsøger at skrive notifikationen + Forsøger at få adgang til placering + Forsøger at lave et telefonopkald + Forsøger at læse SMS/MMS + Forsøger at skrive/ændre SMS/MMS + Forsøger at modtage SMS/MMS + Forsøger at modtage SMS/MMS + Forsøger at modtage SMS/MMS + Forsøger at modtage SMS/MMS + Forsøger at sende SMS/MMS + Forsøger at læse SMS/MMS + Forsøger at skrive/ændre SMS/MMS + Forsøger at ændre indstillinger + Forsøger at trække på toppen + Forsøger at få adgang til notifikationer + Forsøger at få adgang til Kamera + Forsøger at optage lyd + Forsøger at afspille lyd + Forsøger at læse udklipsholderen + Forsøger at ændre udklipsholderen + Forsøger at slå Wifi til/fra + Forsøger at slå bluetooth til/fra + Forsøger at slå mobildata til/fra + Forsøger at planlægge opvågning af enheden + + diff --git a/core/res/res/values-da/cm_strings.xml b/core/res/res/values-da/cm_strings.xml index cd54dd60daf49..822dcedcace12 100644 --- a/core/res/res/values-da/cm_strings.xml +++ b/core/res/res/values-da/cm_strings.xml @@ -46,6 +46,8 @@ Tillader app\'en at kontrollere FM-modtageren. kontrollér FM-modtager Tillader app\'en at kontrollere FM-senderen. + send imiterede SMS-beskeder + Tillader at appen sender imiterede SMS-beskeder. Dette tillader en app at sende SMS-beskeder til betroede apps. Ondsindede apps kan sende beskeder hele tiden, hvilket blokerer enhedens meddelelsessystem og forstyrrer brugeren. Aflader, %d%% Dock SD-kort App lukket @@ -116,11 +118,30 @@ annuller app-underretninger Tillader app\'en at annullere underretninger, oprettet af andre apps. blokér udgående SMS - Tillader app\'en at - blokere en udgående SMS. - Ondsindede apps kan muligvis bruge dette til at forhindre udgående SMS-beskeder. + Tillader app\'en at blokere en udgående SMS. Ondsindede apps kan muligvis bruge dette til at forhindre udgående SMS-beskeder. Håndhæv SELinux Slå håndhævning af SELinux-politikker eller tolerant tilstand til og fra. Håndhæv MMAC Slå håndhævning af MMAC-politikker eller tolerant tilstand til og fra. + Mislykkedes. Aktivér SIM/RUIM-Lås. + Forkert PUK-kode! + PIN-betjening mislykkedes! + PUK-betjening mislykkedes! + Kode Accepteret! + \nResterende Forsøg : + Ugyldigt kort. + SIM-kortet er ugyldigt. + SIM/RUIM er Perso-låst + Intet flytbart medie tilgængeligt\u2026 + USSD-anmodning er ændret til en DIAL-anmodning. + USSD-anmodning er ændret til en SS-anmodning. + USSD-anmodning er ændret til en ny USSD-anmodning. + SS-anmodning er ændret til en DIAL-anmodning. + SS-anmodning er ændret til en USSD-anmodning. + SS-anmodning er ændret til en ny SS-anmodning. + SUB:%d : %s. + %s, %s + Strømbesparelse + Balanceret + Høj ydeevne diff --git a/core/res/res/values-de/cm_arrays.xml b/core/res/res/values-de/cm_arrays.xml new file mode 100644 index 0000000000000..6ac4a707e6cb8 --- /dev/null +++ b/core/res/res/values-de/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + + Versucht den Standort abzurufen + Versucht den Standort abzurufen + Versucht den Standort abzurufen + Versucht Vibrationsalarm zu steuern + Versucht Kontakte zu lesen + Versucht Kontakte zu ändern + Versucht Anrufliste zu lesen + Versucht Anrufliste zu bearbeiten + Versucht Kalendereinträge zu lesen + Versucht Kalendereinträge zu ändern + Versucht den Standort abzurufen + Versucht Benachrichtigung zu erstellen + Versucht den Standort abzurufen + Versucht Anrufe zu tätigen + Versucht SMS/MMS zu lesen + Versucht SMS/MMS zu ändern + Versucht SMS/MMS zu empfangen + Versucht SMS/MMS zu empfangen + Versucht SMS/MMS zu empfangen + Versucht SMS/MMS zu empfangen + Versucht SMS/MMS zu senden + Versucht SMS/MMS zu senden + Versucht SMS/MMS zu ändern + Versucht Einstellungen zu ändern + Versucht an oberste Position zu ziehen + Versucht auf Benachrichtigungen zuzugreifen + Versucht Bilder und Videos aufzunehmen + Versucht Audio aufzunehmen + Versucht Audio wiedergeben + Versucht Zwischenablage zu lesen + Versucht Zwischenablage zu ändern + Versucht WLAN ein-/auszuschalten + Versucht Bluetooth ein-/auszuschalten + Versucht mobile Daten ein-/auszuschalten + Versucht den Ruhezustand zu deaktivieren + + diff --git a/core/res/res/values-de/cm_strings.xml b/core/res/res/values-de/cm_strings.xml index 1c227fdd04f0d..6506f4085eb94 100644 --- a/core/res/res/values-de/cm_strings.xml +++ b/core/res/res/values-de/cm_strings.xml @@ -1,5 +1,6 @@ - + Fehlgeschlagen. Aktivieren Sie die SIM-Kartensperre. + Falscher PUK-Code + Entsperren mit PIN fehlgeschlagen + Entsperren mit PUK fehlgeschlagen + Code akzeptiert + \nVerbleibende Versuche: + Ungültige Karte + SIM-Karte ist ungültig + SIM-Karte ist gesperrt + USSD-Anfrage wurde in DIAL-Anfrage umgewandelt + USSD-Anfrage wurde in SS-Anfrage umgewandelt + USSD-Anfrage wurde in neue USSD-Anfrage umgewandelt + SS-Anfrage wurde in DIAL-Anfrage umgewandelt + SS-Anfrage wurde in USSD-Anfrage umgewandelt + SS-Anfrage wurde in neue SS-Anfrage umgewandelt + Vertrag:%d : %s. + %s, %s diff --git a/core/res/res/values-el/cm_arrays.xml b/core/res/res/values-el/cm_arrays.xml new file mode 100644 index 0000000000000..cc9721988587a --- /dev/null +++ b/core/res/res/values-el/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + Προσπάθεια πρόσβασης τοποθεσίας + Προσπάθεια πρόσβασης τοποθεσίας + Προσπάθεια πρόσβασης τοποθεσίας + Προσπάθεια χρήσης δόνησης + Προσπάθεια ανάγνωσης επαφών + Προσπάθεια τροποποίησης επαφών + Προσπάθεια ανάγνωσης αρχείου κλήσεων + Προσπάθεια τροποποίησης αρχείου κλήσεων + Προσπάθεια ανάγνωσης ημερολογίου + Προσπάθεια τροποποίησης ημερολογίου + Προσπάθεια πρόσβασης τοποθεσίας + Προσπάθεια δημοσίευσης ειδοποίησης + Προσπάθεια πρόσβασης τοποθεσίας + Προσπάθεια τηλεφωνικής κλήσης + Προσπάθεια ανάγνωσης SMS/MMS + Προσπάθεια σύνταξης/τροποποίησης SMS/MMS + Προσπάθεια λήψης SMS/MMS + Προσπάθεια λήψης SMS/MMS + Προσπάθεια λήψης SMS/MMS + Προσπάθεια λήψης SMS/MMS + Προσπάθεια αποστολής SMS/MMS + Προσπάθεια ανάγνωσης SMS/MMS + Προσπάθεια εγγραφής/τροποποίησης SMS/MMS + Προσπάθεια τροποποίησης ρυθμίσεων + Προσπάθεια μεταφοράς στην κορυφή + Προσπάθεια πρόσβασης ειδοποιήσεων + Προσπάθεια πρόσβασης φωτογρ. μηχανής + Προσπάθεια ηχογράφησης + Προσπάθεια αναπαραγωγής ήχου + Προσπάθεια ανάγνωσης πρόχειρου + Προσπάθεια τροποποίησης πρόχειρου + Προσπάθεια ενεργοποίησης/απενεργοποίησης Wi-Fi + Προσπάθεια ενεργοποίησης/απενεργοποίησης Bluetooth + Προσπάθεια ενεργοποίησης/απενεργοποίησης δεδομένων + Προσπάθεια προγραμματισμού αφύπνισης συσκευής + + diff --git a/core/res/res/values-el/cm_strings.xml b/core/res/res/values-el/cm_strings.xml index aa75e151f3b86..59e7424e1d617 100644 --- a/core/res/res/values-el/cm_strings.xml +++ b/core/res/res/values-el/cm_strings.xml @@ -1,5 +1,6 @@ - + + + + 40413496 + -3713379 + + + 6 + + diff --git a/core/res/res/values-es/cm_arrays.xml b/core/res/res/values-es/cm_arrays.xml new file mode 100644 index 0000000000000..c5a8c116bcb14 --- /dev/null +++ b/core/res/res/values-es/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + Intenta acceder a la ubicación + Intenta acceder a la ubicación + Intenta acceder a la ubicación + Intenta utilizar la vibración + Intenta consultar tus contactos + Intenta modificar tus contactos + Intenta leer el registro de llamadas + Intenta modificar el registro de llamadas + Intenta leer eventos de calendario + Intenta añadir o modificar eventos de calendario + Intenta acceder a la ubicación + Intenta publicar una notificación + Intenta acceder a la ubicación + Intenta llamar directamente a un número de teléfono + Intenta leer tus mensajes SMS/MMS + Intenta editar tus mensajes SMS/MMS + Intenta recibir mensajes SMS/MMS + Intenta recibir mensajes SMS/MMS + Intenta recibir mensajes SMS/MMS + Intenta recibir mensajes SMS/MMS + Intenta enviar mensajes SMS/MMS + Intenta leer tus mensajes SMS/MMS + Intenta editar tus mensajes SMS/MMS + Intenta modificar los ajustes del sistema + Intenta mostrarse sobre otras aplicaciones + Intenta acceder a las notificaciones + Intenta realizar fotografías y vídeo + Intenta grabar sonido + Intenta reproducir sonido + Intenta consultar el portapapeles + Intenta modificar el portapapeles + Intenta cambiar el estado de Wi-Fi + Intenta cambiar el estado de Bluetooth + Intenta cambiar el estado de la conexión de red + Intenta programar un encendido del dispositivo + + diff --git a/core/res/res/values-es/cm_strings.xml b/core/res/res/values-es/cm_strings.xml index b84eb0a028f94..fde5b87451ada 100644 --- a/core/res/res/values-es/cm_strings.xml +++ b/core/res/res/values-es/cm_strings.xml @@ -17,6 +17,23 @@ Conversaciones No hay medios extraíbles\u2026 + No realizado. Habilita antes el bloqueo de SIM/RUIM. + ¡Código PUK incorrecto! + ¡Falló la operación de PIN! + ¡Falló la operación de PUK! + ¡Código aceptado! + \nIntentos restantes: + Tarjeta inválida. + La tarjeta SIM no es válida. + SIM/RUIM tiene bloqueo personalizado + Solicitud USSD es modificada a solicitud DIAL. + Solicitud USSD es modificada a solicitud SS. + Solicitud USSD es modificada a nueva solicitud USSD. + Solicitud SS es modificada a solicitud DIAL. + Solicitud SS es modificada a solicitud USSD. + Solicitud SS es modificada a nueva solicitud SS. + SUS:%d : %s. + %s, %s Predeterminado Trabajo Casa @@ -73,4 +90,7 @@ Alternar entre el modo permisivo o restrictivo de SELinux. Forzar MMAC Alternar entre el modo permisivo o restrictivo de MMAC. + Ahorro de energía + Equilibrado + Alto rendimiento diff --git a/core/res/res/values-hu/cm_arrays.xml b/core/res/res/values-hu/cm_arrays.xml new file mode 100644 index 0000000000000..6ca04c4140151 --- /dev/null +++ b/core/res/res/values-hu/cm_arrays.xml @@ -0,0 +1,54 @@ + + + + + Megpróbál hozzáférni a helymeghatározáshoz + Megpróbál hozzáférni a helymeghatározáshoz + Megpróbál hozzáférni a helymeghatározáshoz + Megpróbálja használni a rezgő módot + Megpróbálja olvasni a névjegyeket + Megpróbálja módosítani a névjegyeket + Megpróbálja olvasni a hívásnaplót + Megpróbálja módosítani a hívásnaplót + Megpróbálja olvasni a naptárat + Megpróbálja módosítani a naptárat + Megpróbál hozzáférni a helymeghatározáshoz + Megpróbálja továbbitani a pozíciót + Megpróbál hozzáférni a helymeghatározáshoz + Megpróbál hívást indítani + Megpróbálja olvasni az SMS/MMS-eket + Megpróbálja írni/módosítani az SMS/MMS-eket + Megpróbálja fogadni az SMS/MMS-eket + Megpróbálja fogadni az SMS/MMS-eket + Megpróbálja fogadni az SMS/MMS-eket + Megpróbálja fogadni az SMS/MMS-eket + Megpróbálja küldeni az SMS/MMS-eket + Megpróbálja olvasni az SMS/MMS-eket + Megpróbálja írni/módosítani az SMS/MMS-eket + Megpróbálja módosítani a beállításokat + Megpróbál előtérbe kerülni + Megpróbál hozzáférni az értesítésekhez + Megpróbál hozzáférni a kamerához + Megpróbál hangot rögzíteni + Megpróbál hangot lejátszani + Megpróbálja olvasni a vágólapot + Megpróbálja módosítani a vágólapot + Megpróbálja ki/be kapcsolni a WiFi-t + Megpróbálja ki/be kapcsolni a Bluetooth-t + Megpróbálja ki/be kapcsolni a mobil adatkapcsolatot + Megpróbálja ütemezve felébreszteni az eszközt + + diff --git a/core/res/res/values-hu/cm_strings.xml b/core/res/res/values-hu/cm_strings.xml index 7cae3904636d7..7b2f5c9d6d3c8 100644 --- a/core/res/res/values-hu/cm_strings.xml +++ b/core/res/res/values-hu/cm_strings.xml @@ -71,4 +71,24 @@ MMAC Váltás a biztonságos és az engedélyező MMAC mód között. Nincs eltávolítható memóriakártya\u2026 + Sikertelen. Engedélyezze a SIM/RUIM zárat. + Helytelen PUK kód. + PIN művelet sikertelen! + PUK művelet sikertelen! + Kód elfogadva! + \nHátralévő kisérletek: + Érvénytelen kártya. + Érvénytelen SIM kártya. + SIM/RUIM Perso záras + USSD kérés módosítva DIAL kérésre. + USSD kérés módosítva SS kérésre. + USSD kérés módosítva új USSD kérésre. + SS kérés módosítva DIAL kérésre. + SS kérés módosítva USSD kérésre. + SS kérés módosítva új SS kérésre. + SUB:%d : %s. + %s, %s + Energiatakarékos + Kiegyensúlyozott + Teljesítménycentrikus diff --git a/core/res/res/values-it/cm_arrays.xml b/core/res/res/values-it/cm_arrays.xml new file mode 100644 index 0000000000000..6f3b53efd06fe --- /dev/null +++ b/core/res/res/values-it/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + Tentativo di accesso alla posizione + Tentativo di accesso alla posizione + Tentativo di accesso alla posizione + Tentativo di utilizzo della vibrazione + Tentativo di lettura dei contatti + Tentativo di modifica dei contatti + Tentativo di lettura del registro chiamate + Tentativo di modifica del registro chiamate + Tentativo di lettura calendario + Tentativo di modifica del calendario + Tentativo di accesso alla posizione + Tentativo di pubblicazione notifica + Tentativo di accesso alla posizione + Tentativo di chiamata + Tentativo di lettura SMS/MMS + Tentativo di scrittura/modifica SMS/MMS + Tentativo di ricezione SMS/MMS + Tentativo di ricezione SMS/MMS + Tentativo di ricezione SMS/MMS + Tentativo di ricezione SMS/MMS + Tentativo di invio SMS/MMS + Tentativo di lettura SMS/MMS + Tentativo di scrittura/modifica SMS/MMS + Tentativo di modifica delle impostazioni + Tentativo di tracciare in alto + Tentativo di accesso a notifiche + Tentativo di accesso alla fotocamera + Tentativo di registrazione audio + Tentativo di riproduzione audio + Tentativo di lettura degli appunti + Tentativo di modifica degli appunti + Tentativo di attivazione/disattivazione del Wi-Fi + Tentativo di attivazione/disattivazione del Bluetooth + Tentativo di attivazione/disattivazione dei dati mobili + Tentativo di programmazione risveglio del dispositivo + + diff --git a/core/res/res/values-it/cm_strings.xml b/core/res/res/values-it/cm_strings.xml index d3fb47f1378b6..7fafe526898eb 100644 --- a/core/res/res/values-it/cm_strings.xml +++ b/core/res/res/values-it/cm_strings.xml @@ -14,6 +14,25 @@ limitations under the License. --> + Conversazioni + Non ci sono dispositivi di memoria estraibili\u2026 + Non eseguito. Abilitare prima il blocco SIM/RUIM. + Codice PUK errato! + Errore nell\'operazione PIN! + Errore nell\'operazione PUK! + Codice valido! + \nTentativi rimanenti: + Tessera non valida. + La tessera SIM non è valida. + SIM/RUIM con blocco personalizzato + Richiesta USSD modificata in richiesta DIAL. + Richiesta USSD modificata in richiesta SS. + Richiesta USSD modificata in nuova richiesta USSD. + Richiesta SS modificata in richiesta DIAL. + Richiesta SS modificata in richiesta USSD. + Richiesta SS modificata in nuova richiesta SS. + SUS:%d : %s. + %s, %s Predefinito Lavoro Casa @@ -43,7 +62,6 @@ modifica azione tasto spegnimento Consente a un\'applicazione di cambiare il comportamento del tasto di spegnimento In uso, %d%% - Nessun supporto rimovibile presente\u2026 Applicazione terminata Riavvia Profilo @@ -71,4 +89,7 @@ Alterna tra regole restrittive o permissive di SELinux. Forza MMAC Alterna tra regole restrittive o permissive di MMAC. + Risparmio energetico + Bilanciato + Prestazioni diff --git a/core/res/res/values-lt/cm_arrays.xml b/core/res/res/values-lt/cm_arrays.xml new file mode 100644 index 0000000000000..5c2fb8bfaffc3 --- /dev/null +++ b/core/res/res/values-lt/cm_arrays.xml @@ -0,0 +1,54 @@ + + + + + Bando pasiekti vietovę + Bando pasiekti vietovę + Bando pasiekti vietovę + Bando naudotis vibracija + Bando skaityti kontaktus + Bando keisti kontaktus + Bando skaityti skambučių žurnalą + Bando keisti skambučių žurnalą + Bando skaityti kalendorių + Bando keisti kalendorių + Bando pasiekti vietovę + Bando rašyti pranešimą + Bando pasiekti vietovę + Bando skambinti telefonu + Bando skaityti SMS/MMS + Bando rašyti/keisti SMS/MMS + Bando gauti SMS/MMS + Bando gauti SMS/MMS + Bando gauti SMS/MMS + Bando gauti SMS/MMS + Bando siųsti SMS/MMS + Bando skaityti SMS/MMS + Bando rašyti/keisti SMS/MMS + Bando keisti nustatymus + Bando piešti viršuje + Bando pasiekti pranešimus + Bando pasiekti kamera + Bando įrašyti garso įrašą + Bando groti garso įrašą + Bando skaityti iškarpinę + Bando keisti iškarpinę + Bando įjungti/išjungti Wifi + Bando įjungti/išjungti bluetooth + Bando įjungti/išjungti mobiliuosius duomenis + Bando planuoti kada įrenginys pabus + + diff --git a/core/res/res/values-lt/cm_strings.xml b/core/res/res/values-lt/cm_strings.xml new file mode 100644 index 0000000000000..89f7c8016f4a3 --- /dev/null +++ b/core/res/res/values-lt/cm_strings.xml @@ -0,0 +1,94 @@ + + + + Numatytasis + Darbas + Namai + Tylus + Naktis + Telefonas + Kalendorius + Gmail + El. paštas + Pranešimai + Perkrauti planšetinį kompiuterį + Perkrauti telefoną + Ekranvaizdis + Išplėstas darbastalis + Įjungtas + Išjungtas + Perkrauti + Atkūrimo režimas + \'Bootloader\' + Įjungimo meniu + Greitasis įjungimas + Parsisiuntimo režimas + Perkraunama\u2026 + Jūsų planšetinis kompiuteris persikraus. + Jūsų telefonas persikraus. + Kitas + Perkrauti + Profilis + Ekrano sukimas atrakintas + Ekrano sukimas užrakintas + Programa priverstinai uždaryta + %s nėra įdiegta + Nepaisyti įjungimo klavišo + Leisti programoms nepaisyti įjungimo klavišo + Kontroliuoti FM imtuvą + Leidžia programai valdyti FM imtuvą + Kontroliuoti FM siųstuvą + Leidžia programai valdyti FM siųstuvą + Įjungti arba išjungti privatumo apsaugą + Leisti programai keisti, kai kita programa veikia su privatumo apsauga. Kai programa veikia su privatumo apsauga ji neturės prieigos prie asmeninių duomenų, pavyzdžiui, kontaktų, skambučių žurnalų ar pranešimų. + Perimti siunčiamus SMS + Leisti programai perimti siunčiamus SMS. Kenkėjiškos programos gali tuo pasinaudoti, kad būtų užkirstas kelias siunčiamiems SMS. + Atšaukti programos įspėjimus + Leidžia programai atšaukti įspėjimus, sukurtus kitų programų. + Privatumo apsauga aktyvi + %1$s negalės naudotis asmeniniais duomenimis + Profiliai + Nėra + Iškraunama, %d%% + Atminties kortelės dokas + Siųsti netikrus pranešimus + Leidžia programai siųsti netikrus pranešimus. Tai leidžia programai siųsti pranešimus patikimoms programoms. Kenkėjiškos programos gali siųsti pranešimus nuolat, blokuodamos įrenginio įspėjimų sistemą ir sutrikdant vartotoją + Laikmenos nėra\u2026 + Užtikrinti SELinux + Perjungti SELinux politiką tarp vykdančiojo arba liberalaus. + Užtikrinti MMAC + Perjungti MMAC politiką tarp vykdančiojo arba liberalaus. + Energijos taupymas + Subalansuotas + Našumas + Nesėkmingas. Įjunkite SIM/RUIM Užraktą. + Neteisingas PUK kodas! + PIN operacija nepavyko! + PUK operacija nepavyko! + Kodas priimtas! + \nLikę bandymai: + Negaliojanti kortelė. + Negaliojanti SIM kortelė. + SIM/RUIM kortelė yra užrakinta. + USSD užklausa pakeista dėl DIAL prašymo. + USSD užklausa pakeista dėl SS prašymo. + USSD užklausa pakeista į naują. + SS užklausa pakeista į naują DIAL prašymą. + SS užklausa pakeista į naują USSD prašymą. + SS užklausa pakeista į naują. + PREN.:%d : %s. + %s, %s + diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 593316f2fe292..171363f0b6217 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -158,11 +158,11 @@ "Pranešti apie triktį" "Bus surinkta informacija apie dabartinę įrenginio būseną ir išsiųsta el. pašto pranešimu. Šiek tiek užtruks, kol pranešimas apie triktį bus paruoštas siųsti; būkite kantrūs." "Tylus režimas" - "Garsas IŠJUNGTAS" - "Garsas ĮJUNGTAS" + "Išjungtas" + "Įjungtas" "Lėktuvo režimas" - "ĮJUNGTAS lėktuvo režimas" - "lėktuvo režimas IŠJUNGTAS" + "Įjungtas" + "Išjungtas" "999+" "Saugos režimas" "„Android“ sistema" diff --git a/core/res/res/values-mdpi/dimens.xml b/core/res/res/values-mdpi/dimens.xml index 27786e20774e9..51570c6ca254d 100644 --- a/core/res/res/values-mdpi/dimens.xml +++ b/core/res/res/values-mdpi/dimens.xml @@ -18,335 +18,6 @@ */ --> - - 164dp - - 145dp - - 48dip - - 2 - 64dip - - 25dip - - 48dp - - 48dp - - 42dp - - 24dip - - 48sp - - @dimen/navigation_bar_height - - 32dip - - 5dp - - - 104dp - - 64dp - - 52dp - - 800dp - - - 56dip - - 56dip - - 4dip - - 3dip - - 9dip - - - 270dp - - - 135dip - - - 75dip - - - 40dip - - - 15dip - - - 0dp - - 0dp - - 0dp - - 0dp - - 48dp - - 0dp - - 16dp - - 4 - - 6 - - 0dp - - 0dp - - 0dp - - 65% - - - 320dp - - 320dp - - 80% - - 100% - - - 0dp - - 16dip - 0x02000000 - - 0x02000000 - - 8dip - 8dip - 16dip - - - 95% - - - 64dp - - 64dp - - - 160dip - - - 320dip - - - 64dip - - 48dip - - - 48dip - - 8dip - - 18dp - - 14dp - - -3dp - - 5dip - - - 80dip - - - 14dip - - - 42dip - - - 22dip - - - 12dip - - - 40dip - - - 330dip - - - 200dip - - - 8dip - - - 8dip - - - 8dip - - - 32dip - - - 240dip - - - 80dp - - - 8dp - 8dp - 8dp - 8dp - - - 56dip - - - 48dp - - - 180dp - - - 14dp - - 18dp - - 12dp - - - 25dp - - - - 600dp - - - 480dp - - - 0dp - - - 0dp - - - 75dp - - - 15dp - - - 13dp - - - 16dp - - - -16dp - - - 0dp - - - 0dp - - - 60dp - - - 46dp - - - 7dp - - - 2dp - - - 16dp - - - 0dp - - - 64dp - - - 0dp - - - 80dip - - - 320dp - 300dp - - - 450dp - - - 8dp - - - 8dp - - - 2dp - - - 1dp - - - 66dp - - - 10sp - - - 24dp - - - 600dp - - - 160dp - - - 10dp - diff --git a/core/res/res/values-nl/cm_arrays.xml b/core/res/res/values-nl/cm_arrays.xml new file mode 100644 index 0000000000000..5043aac6a9df9 --- /dev/null +++ b/core/res/res/values-nl/cm_arrays.xml @@ -0,0 +1,56 @@ + + + + + + probeert toegang te krijgen tot locatie + probeert toegang te krijgen tot locatie + probeert toegang te krijgen tot locatie + probeert trillen te gebruiken + probeert contacten te lezen + probeert contacten aan te passen + probeert oproeplogboek te lezen + probeert oproeplogboek aan te passen + probeert agenda te lezen + probeert agenda aan te passen + probeert toegang te krijgen tot locatie + probeert melding te tonen + probeert toegang te krijgen tot locatie + probeert te bellen + probeert sms/mms te lezen + probeert sms/mms te schrijven/wijzigen + probeert sms/mms te ontvangen + probeert sms/mms te ontvangen + probeert sms/mms te ontvangen + probeert sms/mms te ontvangen + probeert sms/mms te verzenden + probeert sms/mms te lezen + probeert sms/mms te schrijven/wijzigen + probeert instellingen aan te passen + probeert op de voorgrond te komen + probeert toegang te krijgen tot meldingen + probeert toegang te krijgen tot Camera + probeert audio op te nemen + probeert audio af te spelen + probeert klembord te lezen + probeert klembord aan te passen + probeert wifi in/uit te schakelen + probeert Bluetooth in/uit te schakelen + probeert mobiele gegevens in/uit te schakelen + probeert apparaat ontwaken in te plannen + + diff --git a/core/res/res/values-nl/cm_strings.xml b/core/res/res/values-nl/cm_strings.xml index 53392bd4d9b9d..17b9f2a1a6e24 100644 --- a/core/res/res/values-nl/cm_strings.xml +++ b/core/res/res/values-nl/cm_strings.xml @@ -1,5 +1,6 @@ - + Mislukt. SIM/RUIM-vergrendeling inschakelen. + Pukcode ongeldig + PIN-bewerking mislukt + PUK-bewerking mislukt + Code correct + \nResterende pogingen: + Kaart ongeldig + Simkaart is ongeldig + SIM/RUIM bevat Perso-vergrendeling. + USSD-verzoek veranderd naar DIAL-verzoek. + USSD-verzoek veranderd naar SS-verzoek. + USSD-verzoek veranderd naar nieuw USSD-verzoek. + SS-verzoek veranderd naar DIAL-verzoek. + SS-verzoek veranderd naar USSD-verzoek. + SS-verzoek veranderd naar nieuw SS-verzoek. + ABON.:%d : %s. + %s, %s diff --git a/core/res/res/values-pl/cm_arrays.xml b/core/res/res/values-pl/cm_arrays.xml new file mode 100644 index 0000000000000..fff2ad0936817 --- /dev/null +++ b/core/res/res/values-pl/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + + Podjęto próbę uzyskania dostępu do lokalizacji + Podjęto próbę uzyskania dostępu do lokalizacji + Podjęto próbę uzyskania dostępu do lokalizacji + Podjęto próbę użycia wibracji + Podjęto próbę odczytania kontaktów + Podjęto próbę zmodyfikowania kontaktów + Podjęto próbę odczytania rejestru połączeń + Podjęto próbę zmodyfikowania rejestru połączeń + Podjęto próbę odczytania kalendarza + Podjęto próbę zmodyfikowania kalendarza + Podjęto próbę uzyskania dostępu do lokalizacji + Podjęto próbę aktywowania powiadomienia + Podjęto próbę uzyskania dostępu do lokalizacji + Podjęto próbę wykonania połączenia głosowego + Podjęto próbę odczytania wiadomości SMS/MMS + Podjęto próbę pisania/edytowania wiadomości SMS/MMS + Podjęto próbę odebrania wiadomości SMS/MMS + Podjęto próbę odebrania wiadomości SMS/MMS + Podjęto próbę odebrania wiadomości SMS/MMS + Podjęto próbę odebrania wiadomości SMS/MMS + Podjęto próbę wysłania wiadomości SMS/MMS + Podjęto próbę odczytania wiadomości SMS/MMS + Podjęto próbę pisania/edytowania wiadomości SMS/MMS + Podjęto próbę modyfikowania ustawień + Podjęto próbę wysunięcia aplikacji na wierzch + Podjęto próbę uzyskania dostępu do powiadomień + Podjęto próbę uzyskania dostępu do kamery + Podjęto próbę nagrywania dźwięku + Podjęto próbę odtwarzania dźwięku + Podjęto próbę odczytania schowka + Podjęto próbę zmodyfikowania schowka + Podjęto próbę przełączenia stanu WiFi + Podjęto próbę przełączenia stanu Bluetooth + Podjęto próbę przełączenia stanu danych mobilnych + Podjęto próbę zaplanowania wybudzenia urządzenia + + diff --git a/core/res/res/values-pl/cm_strings.xml b/core/res/res/values-pl/cm_strings.xml index 8e937e177837b..bea908c2eadac 100644 --- a/core/res/res/values-pl/cm_strings.xml +++ b/core/res/res/values-pl/cm_strings.xml @@ -71,4 +71,24 @@ Przełącza między wymuszaniem polityki SELinux a trybem pobłażliwym. Wymuś MMAC Przełącza między wymuszaniem polityki MMAC a trybem pobłażliwym. + Oszczędzanie energii + Zrównoważony + Wydajność + Żądanie USSD jest modyfikowane na żądanie DIAL. + Żądanie USSD jest modyfikowane na żądanie SS. + Żądanie USSD jest modyfikowane na nowe żądanie USSD. + Żądanie SS jest modyfikowane na żądanie DIAL. + Żądanie SS jest modyfikowane na żądanie USSD. + Żądanie SS jest modyfikowane na nowe żądanie SS. + USŁUGA:%d : %s. + %s, %s + Niepoprawna karta. + Niepoprawna karta SIM. + Karta SIM/RUIM jest zablokowana + \nPozostałe próby: + Niepoprawny kod PUK! + Operacja PIN nie powiodła się! + Operacja PUK nie powiodła się! + Kod zaakceptowany! + Niepowodzenie. Włącz blokadę SIM/RUIM. diff --git a/core/res/res/values-pt/cm_arrays.xml b/core/res/res/values-pt/cm_arrays.xml new file mode 100644 index 0000000000000..ea81364a20d97 --- /dev/null +++ b/core/res/res/values-pt/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + Tentando acessar localização + Tentando acessar localização + Tentando acessar localização + Tentando usar vibração + Tentando ler contatos + Tentando modificar contatos + Tentando ler registros de chamadas + Tentando modificar registro de chamadas + Tentando ler calendário + Tentando modificar calendário + Tentando acessar localizaçãon + Tentando enviar notificação + Tentando acessar localização + Tentando realizar chamadal + Tentando ler SMS/MMS + Tentando escrever/modificar SMS/MMS + Tentando receber SMS/MMS + Tentando receber SMS/MMS + Tentando receber SMS/MMS + Tentando receber SMS/MMS + Tentando enviar SMS/MMS + Tentando ler SMS/MMS + Tentando escrever/modificar SMS/MMS + Tentando modificar configurações + Tentando desenhar no topo + Tentando acessar notificações + Tentando acessar Câmera + Tentando gravar áudio + Tentando reproduzir áudio + Tentando ler área de transferência + Tentando modificar área de transferência + Tentando desligar/ligar Wi-Fi + Tentando ligar/desligar Bluetooth + Tentando ligar/desligar dados + Tentando agendar despertador do dispositivo + + \ No newline at end of file diff --git a/core/res/res/values-pt/cm_strings.xml b/core/res/res/values-pt/cm_strings.xml index 3eee0d1e03975..5a16b2c3b889d 100644 --- a/core/res/res/values-pt/cm_strings.xml +++ b/core/res/res/values-pt/cm_strings.xml @@ -125,4 +125,24 @@ Permite ao aplicativo simular envio de mensagens SMS. Isto permite um aplicativo enviar SMS para aplicações confiáveis. Aplicativos maliciosos pode enviar mensagens continuamente, bloqueando o sistema de notificação do dispositivo e perturbar o usuário. Nenhuma mídia removível presente\u2026 + Economia + Balanceado + Performance + Malsucedido. Ativar Bloqueio SIM/RUIM. + Código PUK incorreto! + Falha na operação PIN! + Falha na operação PUK! + Código aceito! + \nTentativas Restantes : + Cartão inválido. + Cartão SIM é Inválido. + SIM/RUIM é bloqueado por Perso + Requisição USSD foi modificado para requição DIAL. + Requisição USSD foi modificado para requisição SS. + Requisição USSD foi modificado para uma nova requisição USSD. + Requisição SS foi modificado para requisição DIAL. + Requisição SS foi modificado para requisição USSD. + Requisição SS foi modificado para uma nova requisição SS. + ASS:%d : %s. + %s, %s \ No newline at end of file diff --git a/core/res/res/values-ro/cm_strings.xml b/core/res/res/values-ro/cm_strings.xml index 28323f941a286..1bea18a33d0aa 100644 --- a/core/res/res/values-ro/cm_strings.xml +++ b/core/res/res/values-ro/cm_strings.xml @@ -14,29 +14,56 @@ limitations under the License. --> - "Reporniţi tablet PC" - "Reporniţi telefonul" - "Repornire" - "Repornire..." - "Recuperare" - "Telefonul dvs. va reporni." - "General" - "Serviciu" - "Acasă" - "Silenţios" - "Noapte" - "Telefon" - "Calendar" - "Gmail" - "Email" - "SMS" - "Desktop extins" - "Activat" - "Dezactivat" - "Repornire" - "Profil" - "Descărcare, %d%%" - "Rotire Ecran Deblocată" - "Rotire Ecran Blocată" - "Aplicaţie oprită" + Implicit + Serviciu + Acasă + Silențios + Noapte + Telefon + Calendar + SMS + Altele + Reporniți tableta + Reporniți telefonul + Captură de ecran + Suprafață de lucru extinsă + Pornit + Oprit + Repornire + Recuperare + Bootloader + Descărcare + Repornire\u2026 + Tableta va reporni. + Telefonul va reporni. + împiedică butonul pornire/oprire + Permite aplicației sa modifice butonul pornire/oprire + controlează receptorul FM + Permite aplicației să controleze receptorul FM. + controlează transmiţătorul FM + Permite aplicației să controleze transmiţătorul FM. + trimite mesaje SMS de batjocură + Permite aplicației sa trimită mesaje SMS de batjocură. Aceasta permite trimiterea de mesaje SMS către o aplicație de încredere. Aplicațiile rău intenționate pot trimite mesaje in continuu, blocând sistemul de notificare și deranjând utilizatorul. + Descărcare, %d%% + Stand card SD + Aplicație inchisă + Repornire + Profil + Rotirea ecranului deblocată + Rotirea ecranului blocată + %s nu este instalată + pornește sau oprește protejarea confidenţialitații + Permite aplicațiilor rularea in modul Protejarea Confidențialității. Când o aplicație ruleaza in modul Protejarea Confidențialității, nu va avea acces la datele personale cum ar fi contactele, jurnalul de apel sau mesajele. + Protejarea Confidențialității activată + %1$s nu va avea acces la datele personale + Profiluri + Niciunul + anulează notificările aplicației + Permite anularea notificărilor create de alte aplicații. + interceptarea mesajelor SMS trimise + Permite interceptarea mesajelor SMS trimise. Aplicațiile rău intenționate pot folosi asta pentru a impiedica trimiterea mesajelor SMS. + Impune SELinux + Comutare politică SELinux impusă sau permisivă. + Impune MMAC + Comutare politică MMAC impusă sau permisivă. diff --git a/core/res/res/values-ru/cm_arrays.xml b/core/res/res/values-ru/cm_arrays.xml new file mode 100644 index 0000000000000..f0b65d557b3c6 --- /dev/null +++ b/core/res/res/values-ru/cm_arrays.xml @@ -0,0 +1,56 @@ + + + + + + + попытка получения доступа к данным о местоположении + попытка получения доступа к данным о местоположении + попытка получения доступа к данным о местоположении + попытка использования вибрации + попытка чтения данных контактов + попытка изменения контактов + попытка просмотра списка вызовов + попытка изменения списка вызовов + попытка чтения данных календаря + попытка изменения календаря + попытка получения доступа к данным о местоположении + попытка вывода уведомления + попытка получения доступа к данным о местоположении + попытка выполнения телефонного звонка + попытка чтения SMS/MMS + попытка записи SMS/MMS + попытка получения SMS/MMS + попытка получения SMS/MMS + попытка получения SMS/MMS + попытка получения SMS/MMS + попытка отправки SMS/MMS + попытка чтения SMS/MMS + попытка записи SMS/MMS + попытка изменения настроек + попытка отображения поверх других элементов + попытка получения доступа к уведомлениям + попытка использования камеры + попытка записи аудио + попытка воспроизведения аудио + попытка чтения буфера обмена + попытка изменения буфера обмена + попытка включения или выключения Wi-Fi + попытка включения или выключения Bluetooth + попытка включения или выключения Мобильного интернета + попытка создания таймера пробуждения устройства + + diff --git a/core/res/res/values-ru/cm_strings.xml b/core/res/res/values-ru/cm_strings.xml index 8fd3ad1a9cd05..4f3c42af18cf4 100644 --- a/core/res/res/values-ru/cm_strings.xml +++ b/core/res/res/values-ru/cm_strings.xml @@ -45,32 +45,41 @@ Поворот экрана разблокирован Поворот экрана заблокирован %s не установлено - - - Включение или выключение режима инкогнито - Приложение может выбрать, будет ли другое приложение работать в режиме инкогнито. Когда приложение запущено в режиме инкогнито, оно не получает доступ к персональным данным, таким как контакты, сообщения или журнал звонков. - Режим инкогнито активен - %1$s не сможет получать доступ к персональным данным - - + Включение или выключение защищённого режима + Приложение может выбрать, будет ли другое приложение работать в защищённом режиме. Когда приложение запущено в защищённом режиме, оно не получает доступ к персональным данным, таким как контакты, сообщения или журнал звонков. + Защищённый режим включён + «%1$s» не имеет доступа к персональным данным Профили - - Не выбран - - Отключение уведомлений - Разрешить приложению отключать уведомления, созданные другими приложениями. - + Удаление уведомлений + Разрешить приложению удалять уведомления, созданные другими приложениями. Перехват исходящих SMS Разрешить приложению перехватывать исходящие SMS. Вредоносные приложения смогут использовать это, чтобы препятствовать отправке SMS-сообщений. - Показ фиктивных SMS-сообщений Разрешить приложению показывать фиктивные SMS. Вредоносные приложения могут посылать сообщения циклически, блокируя систему уведомлений и раздражая пользователя. - Отсутствует внешний накопитель - Переключение режимов SELinux Переключать принудительный и разрешающий режимы политики SELinux. Переключение режимов MMAC Переключать принудительный и разрешающий режимы политики MMAC. + Экономия энергии + Сбалансированный + Высокая производительность + Карта недействительна. + SIM-карта недействительна. + Неудачно. Включите блокировку SIM/RUIM-карты. + Неверный PUK-код + Разблокировка PIN-кодом не удалась + Разблокировка PUK-кодом не удалась + Код принят + \nОсталось попыток: + USSD-запрос преобразован в DIAL-запрос. + USSD-запрос преобразован в SS-запрос. + USSD-запрос преобразован в новый USSD-запрос. + SS-запрос преобразован в DIAL-запрос. + SS-запрос преобразован в USSD-запрос. + SS-запрос преобразован в новый SS-запрос. + ПОДП.:%d : %s. + %s, %s + SIM/RUIM-карта заблокирована diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index c6b77d5e88278..08e5e8dc11763 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -288,7 +288,7 @@ "Изменение настроек экрана" "Приложение сможет изменять текущую конфигурацию, например, региональные настройки или размер шрифта." "Включение режима громкой связи" - "Приложение сможет включать режим \"Штурман\"." + "Приложение сможет включать режим «Штурман»." "Закрытие других приложений" "Приложение сможет завершать фоновые процессы других приложений. Из-за этого другие приложения могут прекратить работу." "Принудительное закрытие других приложений" @@ -590,7 +590,7 @@ "Отключение функции блокировки экрана" "Приложение сможет отключать блокировку экрана и другие функции защиты. Например, блокировка экрана будет отключаться при получении входящего вызова и включаться после завершения разговора." "Просмотр настроек синхронизации" - "Приложение сможет просматривать настройки синхронизации аккаунта, например определять, включена ли синхронизация для приложения \"Контакты\"." + "Приложение сможет просматривать настройки синхронизации аккаунта, например определять, включена ли синхронизация для приложения «Контакты»." "Включение/выключение синхронизации" "Приложение сможет изменять настройки синхронизации аккаунта. Например, с помощью этого разрешения можно включить в аккаунте синхронизацию контактов." "Просмотр статистики синхронизации" @@ -773,12 +773,12 @@ "Введите пароль" "Введите PIN-код" "Неверный PIN-код." - "Для разблокировки нажмите \"Меню\", а затем 0." + "Для разблокировки нажмите «Меню», а затем 0." "Экстренная служба" "Нет сигнала" "Экран заблокирован." - "Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи." - "Для разблокировки нажмите \"Меню\"." + "Нажмите «Меню», чтобы разблокировать экран или вызвать службу экстренной помощи." + "Для разблокировки нажмите «Меню»." "Введите графический ключ" "Экстренный вызов" "Вернуться к вызову" @@ -841,7 +841,7 @@ "Пусто" "Область разблокировки развернута" "Область разблокировки свернута" - "Виджет \"%1$s\"" + "Виджет «%1$s»" "Выбор аккаунта" "Статус" "Камера" @@ -904,7 +904,7 @@ "Установка будильника" "Приложение сможет настраивать будильник. Функция поддерживается не во всех программах." "Добавление голосовых сообщений" - "Приложение сможет добавлять голосовые сообщения в папку \"Входящие\"." + "Приложение сможет добавлять голосовые сообщения в папку «Входящие»." "Изменение прав доступа к геоданным в браузере" "Приложение сможет изменять настройки доступа к геоданным в браузере. Вредоносные программы смогут таким образом отправлять информацию о местоположении на любые веб-сайты." "Проверка пакетов" @@ -934,22 +934,25 @@ "Удалить запрос" "Отправить запрос" "Голосовой поиск" - "Включить \"Изучение касанием\"?" - "%1$s хочет включить функцию \"Изучение касанием\". Она позволяет прослушивать или просматривать описание элементов, которых вы касаетесь, и управлять планшетным ПК с помощью жестов." - "%1$s хочет включить функцию \"Изучение касанием\". Она позволяет прослушивать или просматривать описание элементов, которых вы касаетесь, и управлять телефоном с помощью жестов." + "Включить «Изучение касанием»?" + "%1$s хочет включить функцию «Изучение касанием». Она позволяет прослушивать или просматривать описание элементов, которых вы касаетесь, и управлять планшетным ПК с помощью жестов." + "%1$s хочет включить функцию «Изучение касанием». Она позволяет прослушивать или просматривать описание элементов, которых вы касаетесь, и управлять телефоном с помощью жестов." "1 месяц назад" "Более месяца назад" - "1 секунду назад" - "%d с. назад" + "%d секунду назад" + "%d секунды назад" + "%d секунд назад" - "1 минуту назад" - "%d мин. назад" + "%d минуту назад" + "%d минуты назад" + "%d минут назад" - "1 час назад" - "%d ч. назад" + "%d час назад" + "%d часа назад" + "%d часов назад" "Последние %d дн." @@ -961,12 +964,14 @@ "%d дн. назад" - "через 1 секунду" - "через %d с." + "через %d секунду" + "через %d секунды" + "через %d секунд" - "через 1 минуту" - "через %d мин." + "через %d минуту" + "через %d минуты" + "через %d минут" "через 1 час" @@ -977,15 +982,12 @@ "через %d дн." - "1 сек. назад" "%d сек. назад" - "1 мин. назад" "%d мин. назад" - "1 час назад" "%d ч. назад" @@ -993,15 +995,12 @@ "%d дн. назад" - "через 1 с." "через %d с." - "через 1 мин." "через %d мин." - "через 1 час" "через %d ч." @@ -1024,16 +1023,19 @@ "г." "г." - "1 сек." - "%d сек." + "%d секунда" + "%d секунды" + "%d секунд" - "1 мин." - "%d мин." + "%d минута" + "%d минуты" + "%d минут" - "1 ч." - "%d ч." + "%d час" + "%d часы" + "%d часов" "Ошибка" "Это видео не предназначено для потокового воспроизведения на данном устройстве." @@ -1061,7 +1063,7 @@ "Операции с текстом" "Заканчивается свободное место" "Некоторые системные функции могут не работать" - "Приложение \"%1$s\" выполняется" + "Приложение «%1$s» выполняется" "Нажмите, чтобы получить дополнительные данные или выключить приложение." "ОК" "Отмена" @@ -1069,24 +1071,24 @@ "Отмена" "Внимание" "Загрузка…" - "I" - "O" + "\u2759" +"\u3007" "Что использовать?" "По умолчанию для этого действия" - "Удаляет настройки по умолчанию в меню \"Настройки > Приложения > Загруженные\"." + "Удаляет настройки по умолчанию в меню «Настройки > Приложения > Загруженные»." "Выберите действие" "Выбор приложения для USB-устройства" "Действие не поддерживается ни в одном приложении." - "В приложении \"%1$s\" произошла ошибка." - "В приложении \"%1$s\" произошла ошибка." + "В приложении «%1$s» произошла ошибка." + "В работе процесса «%1$s» произошла ошибка." - "Приложение \"%2$s\" не отвечает."\n\n"Закрыть его?" - "Приложение \"%1$s\" не отвечает."\n\n"Закрыть его?" - "Приложение \"%1$s\" не отвечает. Закрыть его?" - "Приложение \"%1$s\" не отвечает."\n\n"Закрыть его?" + "Приложение «%2$s» не отвечает."\n\n"Закрыть его?" + "Процесс «%1$s» не отвечает."\n\n"Завершить его?" + "Процесс приложения «%1$s» не отвечает. Завершить его?" + "Процесс «%1$s» не отвечает."\n\n"Завершить его?" "ОК" - "Отзыв" + "Сообщить" "Подождать" "Страница не отвечает."\n\n"Закрыть ее?" "Приложение перенаправлено" @@ -1094,8 +1096,8 @@ "Изначально было запущено приложение %1$s." "Масштаб" "Всегда показывать" - "Включить эту функцию можно в меню \"Настройки > Приложения > Загруженные\"." - "Приложение \"%1$s\" (процесс: %2$s) нарушило собственную политику StrictMode." + "Включить эту функцию можно в меню «Настройки > Приложения > Загруженные»." + "Приложение «%1$s» (процесс: %2$s) нарушило собственную политику StrictMode." "Процесс %1$s нарушил собственную политику StrictMode." "Обновление Android..." "Оптимизация приложения %1$d из %2$d." @@ -1113,7 +1115,7 @@ "Громкость звонка" "Громкость мультимедиа" "Воспроизведение по каналу Bluetooth" - "Выбран режим \"Без звука\"" + "Выбран режим «Без звука»" "Громкость при разговоре" "Громкость при разговоре" "Громкость сигнала предупреждения" @@ -1163,19 +1165,19 @@ "<b>%1$s</b> отправляет большое количество SMS. Разрешить приложению и дальше отправлять сообщения?" "Разрешить" "Запретить" - "Приложение <b>%1$s</b> собирается отправить сообщение на адрес <b>%2$s</b>." + "Приложение «<b>%1$s</b>» собирается отправить сообщение на номер <b>%2$s</b>." "С вашего мобильного счета ""могут быть списаны средства""." "С вашего мобильного счета будут списаны средства." "Отправить" "Отмена" "Запомнить выбранный телефон" - "Это можно изменить позже в разделе настроек \"Приложения\"." + "Это можно изменить позже в разделе настроек «Приложения»." "Всегда разрешать" "Не разрешать" - "SIM-карта удалена" + "SIM-карта извлечена" "Пока вы не вставите действующую SIM-карту, мобильная сеть будет недоступна." "Готово" - "SIM-карта добавлена" + "SIM-карта установлена" "Перезагрузите устройство для доступа к мобильной сети." "Перезапуск" "Настройка времени" @@ -1221,7 +1223,7 @@ "Выберите способ ввода" "Настройка способов ввода" "Физическая клавиатура" - "Аппаратура" + "Устройство" "Выберите раскладку клавиатуры" "Нажмите, чтобы выбрать раскладку клавиатуры." " ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -1264,7 +1266,7 @@ "Отправить" "Далее" "Готово" - "Пред." + "Назад" "Выполнить" "Набрать номер"\n"%s" "Создать контакт"\n"с номером %s" @@ -1273,7 +1275,7 @@ "Запрос доступа" "Разрешить" "Отклонить" - "Разрешение запрошено" + "Запрос разрешения" "Требуется разрешение"\n"для аккаунта %s." "Способ ввода" "Синхр." @@ -1293,7 +1295,7 @@ "Не выбран файл" "Сбросить" "Отправить" - "Включен режим \"Штурман\"" + "Включен режим «Штурман»" "Чтобы выйти, нажмите здесь." "USB-модем/точка доступа Wi-Fi используется" "Нажмите для настройки." @@ -1367,7 +1369,7 @@ "Клавиша ввода" "Выберите приложение" "Открыть доступ:" - "Открыть доступ приложению \"%s\"" + "Открыть доступ приложению «%s»" "Перетаскиваемый значок блокировки. Нажмите и удерживайте." "Проведите вверх, чтобы %s." "Проведите вниз, чтобы %s." @@ -1394,7 +1396,7 @@ "Проверьте трафик и настройки." "Передача данных 2G/3G отключена" "Передача данных 4G отключена" - "Моб. Интернет отключен" + "Моб. интернет отключен" "Передача данных через Wi-Fi отключена" "Нажмите, чтобы включить." "Превышен лимита трафика 2G и 3G" diff --git a/core/res/res/values-sk/cm_strings.xml b/core/res/res/values-sk/cm_strings.xml index 3278a7be23618..2ad0a461d4436 100644 --- a/core/res/res/values-sk/cm_strings.xml +++ b/core/res/res/values-sk/cm_strings.xml @@ -16,14 +16,14 @@ Predvolený - V práci + Práca Doma Tichý - Nočný + Noc Telefón Kalendár Gmail - Email + E-mail SMS Iný Reštart tabletu @@ -52,8 +52,8 @@ Aplikácia bola ukončená Reštartovať Profil - Otočenie obrazovky odomknuté - Otočenie obrazovky uzamknuté + Otáčanie obrazovky odomknuté + Otáčanie obrazovky uzamknuté Aplikácia %s nie je nainštalovaná povoliť alebo zakázať ochranu súkromia Umožňuje aplikácii určiť, či má byť iná aplikácia spustená s ochranou súkromia. Keď je aplikácia spustená s ochranou súkromia, nemá umožnený prístup k osobným údajom ako sú kontakty, denníky hovorov, alebo správy. @@ -69,7 +69,27 @@ Prepnúť stav zabezpečenia SELinux. vynútiť MMAC Prepnúť stav zabezpečenia MMAC. - poslať falošné SMS správy + posielať falošné SMS správy Povolí aplikácii odosielať falošné SMS správy. Toto povolenie umožňuje aplikácii posielať SMS do dôveryhodných aplikácií. Škodlivé aplikácie môžu posielať správy nepretržite, blokovať oznamovací systém zariadenia a vyrušovať užívateľa. Nie je dostupné žiadne vymeniteľné médium\u2026 + Neúspešné. Povoľte zámok SIM/RUIM. + Nesprávny kód PUK! + Operácia PIN zlyhala! + Operácia PUK zlyhala! + Kód prijatý! + \nZostáva pokusov: + Neplatná karta. + Karta SIM je neplatná. + Karta SIM/RUIM je zamknutá + Požiadavka USSD je upravená na požiadavku DIAL. + Požiadavka USSD je upravená na požiadavku SS. + Požiadavka USSD je upravená na novú požiadavku USSD. + Požiadavka SS je upravená na požiadavku DIAL. + Požiadavka SS je upravená na požiadavku USSD. + Požiadavka SS je upravená na novú požiadavku SS. + Odber %d: %s. + %s, %s + Úsporný + Vyvážený + Výkonný diff --git a/core/res/res/values-zh-rCN/cm_arrays.xml b/core/res/res/values-zh-rCN/cm_arrays.xml new file mode 100644 index 0000000000000..f8ab955f2f3a7 --- /dev/null +++ b/core/res/res/values-zh-rCN/cm_arrays.xml @@ -0,0 +1,55 @@ + + + + + + 试图访问位置 + 试图访问位置 + 试图访问位置 + 试图使用震动 + 试图读取联系人 + 试图修改联系人 + 试图读取通话记录 + 试图修改通话记录 + 试图读取日历 + 试图修改日历 + 试图访问位置 + 试图传递通知 + 试图访问位置 + 试图打电话 + 试图读取 短信/彩信 + 试图 写/修改 短信/彩信 + 试图接收 短信/彩信 + 试图接收 短信/彩信 + 试图接收 短信/彩信 + 试图接收 短信/彩信 + 试图发送 短信/彩信 + 试图读取 短信/彩信 + 试图 写/修改 短信/彩信 + 试图修改设置 + 试图顶部绘制 + 试图访问通知 + 试图访问相机 + 试图录制音频 + 试图播放音频 + 试图读取剪贴板 + 试图修改剪贴板 + 试图 打开/关闭 Wifi + 试图 打开/关闭 蓝牙 + 试图 打开/关闭 移动数据 + 试图 延迟设备唤醒 + + diff --git a/core/res/res/values-zh-rCN/cm_strings.xml b/core/res/res/values-zh-rCN/cm_strings.xml index 75646a836ab12..3ad355fe1e0c6 100644 --- a/core/res/res/values-zh-rCN/cm_strings.xml +++ b/core/res/res/values-zh-rCN/cm_strings.xml @@ -14,6 +14,21 @@ limitations under the License. --> + 不成功。开启 SIM/RUIM 锁 + 不正确的 PUK 码! + PIN 操作失败! + PUK 操作失败! + 代码已接受! + \n剩余尝试次数: + 无效的卡 + SIM 卡是无效的 + SIM/RUIM 卡已被他人锁定 + USSD 请求已被修改为拨号请求。 + USSD 请求已被修改为 SS 请求。 + USSD 请求已被修改为一个新的 USSD 请求。 + SS 请求已被修改为拨号请求。 + SS 请求已被修改为 USSD 请求。 + SS 请求已被修改为一个新的 SS 请求。 默认 工作 住宅 @@ -47,9 +62,9 @@ 控制 FM 发射器 允许应用控制 FM 发射器。 发送模拟短信 - 允许应用发送模拟短信. - 这允许应用发送短信到受信任的应用. 恶意应用可能连续发送短信, 阻断设备通知系统并扰乱用户. - 正在放电, %d%% + 允许应用发送模拟短信。 + 这允许应用发送短信到受信任的应用。恶意应用可能连续发送短信,阻断设备通知系统并扰乱用户。 + 正在放电,%d%% 没有可移动媒体\u2026 底座 SD 卡 应用程序已被终止 @@ -59,17 +74,17 @@ 屏幕方向已锁定 %s 尚未被安装 启用或禁用隐私防护 - 允许应用更改其他应用是否启用隐私防护. 当一个应用程序运行时启了用隐私防护, 它将不能访问个人数据, 如联系人, 通话记录或者短信. + 允许应用更改其他应用是否启用隐私防护。当一个应用程序运行时启了用隐私防护,它将不能访问个人数据,如联系人,通话记录或者短信。 隐私防护激活 %1$s 将不能访问个人数据 情景模式 取消应用程序通知 - 允许应用取消由其他应用创建的通知. + 允许应用取消由其他应用创建的通知。 拦截向外发送短信 - 允许应用拦截发送短信. 恶意应用可能会使用该功能阻止向外发送短信. + 允许应用拦截发送短信。恶意应用可能会使用该功能阻止向外发送短信。 强制执行 SELinux - 切换 SELinux 策略为强制执行或许可模式. + 切换 SELinux 策略为强制执行或许可模式。 强制执行 MMAC - 切换 MMAC 策略为强制执行或许可模式. + 切换 MMAC 策略为强制执行或许可模式。 diff --git a/core/res/res/values-zh-rTW/cm_strings.xml b/core/res/res/values-zh-rTW/cm_strings.xml index 1fbe225861db5..bfd497f278b85 100644 --- a/core/res/res/values-zh-rTW/cm_strings.xml +++ b/core/res/res/values-zh-rTW/cm_strings.xml @@ -1,5 +1,5 @@ - - 預設 工作 @@ -26,12 +25,12 @@ 電子郵件 簡訊 其他 - 重新啟動平板 + 重新啟動平板電腦 重新啟動手機 - 螢幕截圖 + 螢幕擷取 擴展桌面 啟用 - 禁用 + 停用 重新啟動 Recovery Bootloader @@ -39,22 +38,25 @@ Fastboot Download 正在重新啟動\u2026 - 您的平板將會重新啟動。 + 您的平板電腦將會重新啟動。 您的手機將會重新啟動。 防止使用電源鍵 - 允許應用程式覆蓋電源鍵的功能。 - 控制電台接收器 - 允許應用程式控制電台接收器。 - 控制電台發送器 - 允許應用程式控制電台發送器。 - 放電中, %d%% - 底座SD卡 + 允許應用程式覆寫電源鍵的功能。 + 控制 FM 接收器 + 允許應用程式控制 FM 接收器。 + 控制 FM 發送器 + 允許應用程式控制 FM 發送器。 + 傳送模擬簡訊 + 允許應用程式傳送模擬簡訊,這會允許應用程式傳送簡訊給信任的應用程式。惡意應用程式可能會不斷地傳送訊息,妨礙裝置的通知系統以及干擾使用者。 + 非充電中,%d%% + 沒有可移除的媒體\u2026 + 座架 SD 卡 應用程式已終止 重新啟動 設定檔 顯示旋轉已解鎖 顯示旋轉已鎖定 - %s未安裝 + 未安裝 %s \u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5\u0104\u0100 \u00C7\u0106\u010C \u010E @@ -109,17 +111,20 @@ \u2264\u00ab\u2039 \u2265\u00bb\u203a 啟用或停用隱私守衛 - 允許此應用程式向另一個與隱私守衛一起運行的應用程式作出更改。當應用程式與隱私守衛一起運行時,應用程式將無法存取個人資料,如聯絡人,通話記錄,或簡訊。 - 隱私守衛啟用中 + 允許應用程式變更其他應用程式執行時是否啟用隱私守衛。當應用程式執行時啟用了隱私守衛,應用程式將無法存取個人資料,例如聯絡人、通話記錄、或訊息。 + 隱私守衛作用中 %1$s 將無法存取個人資料 設定檔 取消應用程式通知 - 允許此應用程式取消其他應用程式建立的通知。 - 攔截外撥簡訊 - 允許此應用程式攔截外撥簡訊。惡意應用程式可能會利用此去防止發送外撥簡訊。 - 強制執行 SELinux - 切換 SELinux 政策強制執行模式或許可模式。 - 強制執行 MMAC - 切換 MMAC 政策強制執行模式或許可模式。 + 允許應用程式取消其他應用程式建立的通知。 + 攔截送出簡訊 + 允許應用程式攔截送出簡訊。惡意應用程式可能會利用此去阻止送出簡訊。 + 執行 SELinux + 切換 SELinux 政策執行模式或許可模式。 + 執行 MMAC + 切換 MMAC 政策執行模式或許可模式。 + 省電 + 平衡 + 效能 diff --git a/core/res/res/values/cm_arrays.xml b/core/res/res/values/cm_arrays.xml index ad8c044aa207f..6ab3088ddaa02 100644 --- a/core/res/res/values/cm_arrays.xml +++ b/core/res/res/values/cm_arrays.xml @@ -39,4 +39,55 @@ com.google.android.gsf|com.google.android.talk + + + @string/perf_profile_pwrsv + @string/perf_profile_bal + @string/perf_profile_perf + + + + 0 + 1 + 2 + + + + + Trying to access location + Trying to access location + Trying to access location + Trying to use vibrate + Trying to read contacts + Trying to modify contacts + Trying to read call log + Trying to modify call log + Trying to read calendar + Trying to modify calendar + Trying to access location + Trying to post notification + Trying to access location + Trying to make phone call + Trying to read SMS/MMS + Trying to write/modify SMS/MMS + Trying to receive SMS/MMS + Trying to receive SMS/MMS + Trying to receive SMS/MMS + Trying to receive SMS/MMS + Trying to send SMS/MMS + Trying to read SMS/MMS + Trying to write/modify SMS/MMS + Trying to modify settings + Trying to draw on top + Trying to access notifications + Trying to access Camera + Trying to record audio + Trying to play audio + Trying to read clipboard + Trying to modify clipboard + Trying to turn on/off Wifi + Trying to turn on/off bluetooth + Trying to turn on/off mobile data + Trying to schedule device wakeup + diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml index 536ea194c6c57..d04d93c0feb69 100644 --- a/core/res/res/values/cm_strings.xml +++ b/core/res/res/values/cm_strings.xml @@ -88,9 +88,6 @@ Discharging, %d%% - - No removable media present\u2026 - Dock SD card @@ -196,4 +193,9 @@ Toggle MMAC policy enforcing or permissive mode. + + Power save + Balanced + Performance + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 24b6d269dc5aa..8d784d8a8d53c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2,6 +2,8 @@ - - true - false @@ -306,10 +306,6 @@ Default value is 2 minutes. --> 120000 - - false - false @@ -391,14 +387,6 @@ a car dock make the accelerometer more a physical input (like a lid). --> true - - false - - - false - false - - false - - - false - 2875 @@ -551,9 +533,6 @@ false - - false - true @@ -651,10 +630,6 @@ - 255 - 0 - false - @@ -749,9 +724,6 @@ com.android.location.fused - - com.qualcomm.services.location - true @@ -803,9 +775,6 @@ cell broadcasting sms, and MMS. --> true - - false - true @@ -813,19 +782,6 @@ 8.8.8.8 - - - - - - - - http://google.com - @@ -917,14 +873,6 @@ false - - - - false @@ -1068,6 +1016,10 @@ false + + + true + true @@ -1160,16 +1112,6 @@ 100 - - - 0 - 50 - 100 - 50 - - false @@ -1205,6 +1147,75 @@ com.android.inputmethod.latin + + + + + + + + + + http://google.com + + + + + true + + + false + + + false + + + false + + + false + + + false + + + false + + 255 + 0 + false + + + com.qualcomm.services.location + + + false + + + + + + + + 0 + 50 + 100 + 50 + + true @@ -1240,6 +1251,10 @@ config to 7. --> 15 + + false + @@ -1293,7 +1308,31 @@ false + + false + + + + false + + + false + + + + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a5ebaf3466b71..7497f5a1b4196 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -733,6 +733,11 @@ + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 04e8fcd343b30..0104a36236da1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -97,6 +97,8 @@ the SIM card. --> Your SIM card is PUK-locked. Type the PUK code to unlock it. Type PUK2 to unblock SIM card. + + Unsuccessful. Enable SIM/RUIM Lock. IMEI @@ -2126,6 +2128,25 @@ to unlock the keyguard. Displayed in one line in a large font. --> Incorrect PIN code. + + Incorrect PUK code! + + + PIN operation failed! + + + PUK operation failed! + + + + Code Accepted! + + + \nAttempts Remaining : + To unlock, press Menu then 0. @@ -2205,20 +2226,27 @@ Emergency calls only + + Invalid Card. + + SIM card is Invalid. + Network locked + + SIM/RUIM is Perso locked SIM card is PUK-locked. - See the User Guide or contact Customer Care. + PIN1 Blocked. Enter PUK1.\nor\nPlease see the User Guide or contact Customer Care. - - SIM card is locked. + + SIM/RUIM card is locked. Unmounting SD card\u2026 + + No removable media present\u2026 Erasing USB storage\u2026 @@ -4112,4 +4142,20 @@ No application found to handle this action Revoke + + + USSD request is modified to DIAL request. + USSD request is modified to SS request. + USSD request is modified to new USSD request. + SS request is modified to DIAL request. + SS request is modified to USSD request. + SS request is modified to new SS request. + + + + SUB:%d : %s. + + + %s, %s diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9efa842eff59a..12f1e74ca01be 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1,6 +1,9 @@ + + + + + + + + + + + + @@ -1888,6 +1928,7 @@ + @@ -2037,6 +2078,9 @@ + + + @@ -2051,4 +2095,19 @@ + + + + + + + + + + + + + + + diff --git a/data/sounds/ringtones/ogg/Bootes.ogg b/data/sounds/ringtones/ogg/Bootes.ogg index 46342940ff086..14a9123fb5322 100644 Binary files a/data/sounds/ringtones/ogg/Bootes.ogg and b/data/sounds/ringtones/ogg/Bootes.ogg differ diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 3f256ddd5e9fe..7c04453c5a64b 100755 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1015,7 +1015,17 @@ void OpenGLRenderer::composeLayer(sp current, sp previous) { } } else if (!rect.isEmpty()) { dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); + +#ifdef QCOM_BSP + save(0); + // the layer contains screen buffer content that shouldn't be alpha modulated + // (and any necessary alpha modulation was handled drawing into the layer) + mSnapshot->alpha = 1.0f; +#endif composeLayerRect(layer, rect, true); +#ifdef QCOM_BSP + restore(); +#endif } dirtyClip(); @@ -1659,6 +1669,12 @@ void OpenGLRenderer::setupDraw(bool clear) { setScissorFromClip(); } setStencilFromClip(); +#ifdef QCOM_BSP + } else { + // Disable stencil test in case setStencilFromClip() + // enabled the stencil test but didn't disable it + glDisable(GL_STENCIL_TEST); +#endif } mDescription.reset(); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index ca9471c6f7b3d..4eb7b39bf560b 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +44,8 @@ import java.util.HashMap; +import com.android.internal.util.cm.QuietHoursUtils; + /** * AudioManager provides access to volume and ringer mode control. *

@@ -1475,6 +1480,61 @@ public int getMode() { */ public static final int MODE_IN_COMMUNICATION = AudioSystem.MODE_IN_COMMUNICATION; + /* Calls states for Voice calls */ + /** + * @hide Call state for inactive call state. + */ + public static final int CALL_INACTIVE = AudioSystem.CALL_INACTIVE; + /** + * @hide Call state for active call state. + */ + public static final int CALL_ACTIVE = AudioSystem.CALL_ACTIVE; + /** + * @hide Call state for hold call state. + */ + public static final int CALL_HOLD = AudioSystem.CALL_HOLD; + /** + * @hide Call state for local call hold state. + */ + public static final int CALL_LOCAL_HOLD = AudioSystem.CALL_LOCAL_HOLD; + + + /* VSIDS for IMS, Multimode CS call and GSM CS call */ + /** + * @hide VSID for CS call, Multimode. + */ + public static final long VOICE_VSID = AudioSystem.VOICE_VSID; + /** + * @hide VSID for CS call, GSM-only. + */ + public static final long VOICE2_VSID = AudioSystem.VOICE2_VSID; + /** + * @hide VSID for IMS call, Multimode. + */ + public static final long IMS_VSID = AudioSystem.IMS_VSID; + /** + * @hide VSID for QCHAT call. + */ + public static final long QCHAT_VSID = AudioSystem.QCHAT_VSID; + + + /* Key used in setParameters for VSID and Call_state */ + /** + * @hide Key for vsid used in setParameters. + */ + public static final String VSID_KEY = AudioSystem.VSID_KEY; + + /** + * @hide Key for call_state used in setParameters. + */ + public static final String CALL_STATE_KEY = AudioSystem.CALL_STATE_KEY; + + /** + * @hide Key for all_call_states used in getParameters. + */ + public static final String ALL_CALL_STATES_KEY = AudioSystem.ALL_CALL_STATES_KEY; + + /* Routing bits for setRouting/getRouting API */ /** * Routing audio output to earpiece @@ -1623,7 +1683,12 @@ public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) { * */ public void setParameters(String keyValuePairs) { - AudioSystem.setParameters(keyValuePairs); + IAudioService service = getService(); + try { + service.setParameters(keyValuePairs); + } catch (RemoteException e) { + Log.e(TAG, "Error in setParameters due to "+e); + } } /** @@ -1708,6 +1773,10 @@ public void playSoundEffect(int effectType) { return; } + if (QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_SYSTEM)) { + return; + } + if (!querySoundEffectsEnabled()) { return; } @@ -1751,6 +1820,7 @@ public void playSoundEffect(int effectType, float volume) { } } + /** * Settings has an in memory cache, so this is fast. */ diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 98c52c064b578..84599c448be00 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -444,7 +444,8 @@ public void onError(int error) { // Devices for which the volume is fixed and VolumePanel slider should be disabled final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET | - AudioSystem.DEVICE_OUT_ALL_USB; + AudioSystem.DEVICE_OUT_ALL_USB | + AudioSystem.DEVICE_OUT_PROXY; // TODO merge orientation and rotation private final boolean mMonitorOrientation; @@ -3741,6 +3742,9 @@ private class SettingsObserver extends ContentObserver { @Override public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange, uri); + if (uri == null) { + return; + } // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode. // However there appear to be some missing locks around mRingerModeMutedStreams // and mRingerModeAffectedStreams, so will leave this synchronized for now. @@ -4522,11 +4526,10 @@ private void discardAudioFocusOwner() { private void notifyTopOfAudioFocusStack() { // notify the top of the stack it gained focus if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { - String clientId = mFocusStack.peek().mClientId; - if (canReassignAudioFocusTo(clientId)) { + if (canReassignAudioFocus(mFocusStack.peek().mClientId)) { try { mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( - AudioManager.AUDIOFOCUS_GAIN, clientId); + AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); } catch (RemoteException e) { Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); e.printStackTrace(); @@ -4676,15 +4679,98 @@ private void removeFocusStackEntryForClient(IBinder cb) { /** * Helper function: - * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. + * Gets the call states from HAL using the AudioSystem.getParameters() function, + * parses the return string and updates the local call state variables. + * Returns true if the system is in a state where the focus can be granted to + * QCHAT call, false otherwise. */ - private boolean canReassignAudioFocusTo(String clientId) { - // focus requests are rejected during a phone call or when the phone is ringing - // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus - if (IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { + private boolean isQchatCallAllowed() + { + int voiceCallState = 0; + int voice2CallState = 0; + int volteCallState = 0; + int qchatCallState = 0; + + try { + String keyValuePair, value; + String[] fields, tokens; + long vsid; + int callState; + + // AudioSystem.getParameters() returns a value in the format: + // "all_call_states=281022464:1,282857472:1,281026560:1,276836352:1" + keyValuePair = AudioSystem.getParameters(AudioManager.ALL_CALL_STATES_KEY); + if (!keyValuePair.startsWith(AudioManager.ALL_CALL_STATES_KEY)) { + return false; + } + + value = keyValuePair.substring(AudioManager.ALL_CALL_STATES_KEY.length()+1); + fields = value.split(","); + for (String item : fields) { + tokens = item.split(":"); + vsid = Long.decode(tokens[0]).longValue(); + callState = Integer.decode(tokens[1]).intValue(); + + if (vsid == AudioManager.VOICE_VSID) + voiceCallState = callState; + else if (vsid == AudioManager.VOICE2_VSID) + voice2CallState = callState; + else if (vsid == AudioManager.IMS_VSID) + volteCallState = callState; + else if (vsid == AudioManager.QCHAT_VSID) + qchatCallState = callState; + else + Log.e(TAG, "Unrecognized VSID value "+vsid); + } + + Log.i(TAG, "voiceCallState=" + voiceCallState + " voice2CallState=" + voice2CallState); + Log.i(TAG, "volteCallState=" + volteCallState + " qchatCallState=" + qchatCallState); + } catch (Exception e) { + Log.e(TAG, "Could not get call states from HAL due to "+e); + } + + if ((voiceCallState == AudioManager.CALL_ACTIVE ) || + (voice2CallState == AudioManager.CALL_ACTIVE ) || + (volteCallState == AudioManager.CALL_ACTIVE ) || + (qchatCallState == AudioManager.CALL_ACTIVE )) { + return false; + } + else { return true; } + } + + /* Constant to identify focus stack entry that is used to hold the audio focus for QCHAT client + */ + private static final String CLIENT_ID_QCHAT = "QCHAT"; + + /** + * Helper function: + * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. + */ + private boolean canReassignAudioFocus(String clientId) { + // focus requests are rejected during a phone call or when the phone is ringing + // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus. + // Also focus request is granted to QCHAT client when the CS/IMS calls are on HOLD. if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { + if (clientId.contains(CLIENT_ID_QCHAT) && isQchatCallAllowed()) + return true; + else + return false; + } + return true; + } + + /** + * Helper function: + * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. + */ + private boolean canReassignAudioFocusFromQchat(int streamType, String clientId) { + // Focus request is rejected for Music Player and for QChat client if the focus is already + // acquired by a QChat client + if (!mFocusStack.isEmpty() && + mFocusStack.peek().mClientId.contains(CLIENT_ID_QCHAT) && + (clientId.contains(CLIENT_ID_QCHAT) || (streamType == AudioManager.STREAM_MUSIC))) { return false; } return true; @@ -4728,7 +4814,10 @@ public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb } synchronized(mAudioFocusLock) { - if (!canReassignAudioFocusTo(clientId)) { + if (!canReassignAudioFocus(clientId)) { + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + if (!canReassignAudioFocusFromQchat(mainStreamType, clientId)) { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } @@ -4813,6 +4902,82 @@ public void unregisterAudioFocusClient(String clientId) { } } + /** + * Helper function: + * Parses the input Key-Value pairs and handles the audio focus to QCHAT if needed. + */ + private void handleFocusForQchat(String keyValuePairs) { + long vsid = 0; + int callState = 0; + // Parse VSID and Call State from the input string + try { + String[] pairs; + String[] fields; + + pairs = keyValuePairs.split(";"); + for (String item : pairs) { + Log.d(TAG, "keyValuePair is "+item); + fields = item.split("="); + if (item.startsWith(AudioManager.VSID_KEY)) { + vsid = Long.decode(fields[1]).longValue(); + } + else if (item.startsWith(AudioManager.CALL_STATE_KEY)) { + callState = Integer.decode(fields[1]).intValue(); + } + } + } catch (Exception e) { + Log.e(TAG, "Invalid number format "+e); + return; + } + + // Check if any of the telephony calls are UN-HOLD so that + // QCHAT call should lose focus automatically + if (((vsid == AudioManager.VOICE_VSID) || + (vsid == AudioManager.VOICE2_VSID) || + (vsid == AudioManager.IMS_VSID)) && + (callState == AudioManager.CALL_ACTIVE)) { + + Log.d(TAG, "Telephony call is going to ACTIVE state, see if QCHAT needs to be closed "); + // Notify the QCHAT client that it is losing focus if it is the + // current top of the stack; Also remove the focus stack entry for QCHAT. + synchronized(mAudioFocusLock) { + if ((!mFocusStack.isEmpty()) && + (mFocusStack.peek().mClientId.contains(CLIENT_ID_QCHAT)) && + (mFocusStack.peek().mFocusDispatcher != null)) { + try { + Log.d(TAG, "Dispatch focus loss to QCHAT "); + mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( + -1 * mFocusStack.peek().mFocusChangeType, /* loss and gain codes + are inverse of each other */ + mFocusStack.peek().mClientId); + } catch (RemoteException e) { + Log.e(TAG, " Failure to signal loss of focus due to "+ e); + e.printStackTrace(); + } + + FocusStackEntry fse = mFocusStack.pop(); + fse.unlinkToDeath(); + } + } + } + } + + /** + * Sets a variable number of parameter values to audio hardware. + * + * @param keyValuePairs list of parameters key value pairs in the form: + * key1=value1;key2=value2;... + * + */ + public void setParameters(String keyValuePairs) { + + if (keyValuePairs.contains(AudioManager.VSID_KEY)) { + handleFocusForQchat(keyValuePairs); + } + + AudioSystem.setParameters(keyValuePairs); + } + //========================================================================================== // RemoteControl diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 5e88ba5fa2ba7..3a6c0af9ff173 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -89,6 +92,34 @@ public class AudioSystem public static final int NUM_MODES = 4; + /* Call states for Voice calls */ + /* @hide Call state for inactive call state. */ + public static final int CALL_INACTIVE = 0x1; + /* @hide Call state for active call state. */ + public static final int CALL_ACTIVE = 0x2; + /* @hide Call state for hold call state. */ + public static final int CALL_HOLD = 0x3; + /* @hide Call state for local hold call state. */ + public static final int CALL_LOCAL_HOLD = 0x4; + + /* @hide VSID for CS voice, Multimode */ + public static final long VOICE_VSID = 0x10C01000; + /* @hide VSID for CS Voice GSM-Only */ + public static final long VOICE2_VSID = 0x10DC1000; + /* @hide VSID for IMS Multimode */ + public static final long IMS_VSID = 0x10C02000; + /* @hide VSID for QCHAT */ + public static final long QCHAT_VSID = 0x10803000; + + /* @hide Key for vsid used in setParameters */ + public static final String VSID_KEY = "vsid"; + + /* @hide Key for call_state used in setParameters */ + public static final String CALL_STATE_KEY = "call_state"; + + /* @hide Key for all_call_states used in getParameters */ + public static final String ALL_CALL_STATES_KEY = "all_call_states"; + /* Routing bits for the former setRouting/getRouting API */ /** @deprecated */ @Deprecated public static final int ROUTE_EARPIECE = (1 << 0); @@ -232,6 +263,8 @@ private static void errorCallbackFromNative(int error) public static final int DEVICE_OUT_ANC_HEADSET = 0x10000; public static final int DEVICE_OUT_ANC_HEADPHONE = 0x20000; public static final int DEVICE_OUT_PROXY = 0x40000; + public static final int DEVICE_OUT_FM = 0x80000; + public static final int DEVICE_OUT_FM_TX = 0x100000; public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT; public static final int DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | @@ -253,6 +286,8 @@ private static void errorCallbackFromNative(int error) DEVICE_OUT_ANC_HEADSET | DEVICE_OUT_ANC_HEADPHONE | DEVICE_OUT_PROXY | + DEVICE_OUT_FM | + DEVICE_OUT_FM_TX | DEVICE_OUT_DEFAULT); public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | @@ -279,6 +314,8 @@ private static void errorCallbackFromNative(int error) public static final int DEVICE_IN_USB_DEVICE = DEVICE_BIT_IN | 0x1000; public static final int DEVICE_IN_ANC_HEADSET = DEVICE_BIT_IN | 0x2000; public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x4000; + public static final int DEVICE_IN_FM_RX = DEVICE_BIT_IN | 0x8000; + public static final int DEVICE_IN_FM_RX_A2DP = DEVICE_BIT_IN | 0x10000; public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT; public static final int DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | @@ -296,6 +333,8 @@ private static void errorCallbackFromNative(int error) DEVICE_IN_USB_DEVICE | DEVICE_IN_ANC_HEADSET | DEVICE_IN_PROXY | + DEVICE_IN_FM_RX | + DEVICE_IN_FM_RX_A2DP | DEVICE_IN_DEFAULT); public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET; @@ -323,6 +362,8 @@ private static void errorCallbackFromNative(int error) public static final String DEVICE_OUT_ANC_HEADSET_NAME = "anc_headset"; public static final String DEVICE_OUT_ANC_HEADPHONE_NAME = "anc_headphone"; public static final String DEVICE_OUT_PROXY_NAME = "proxy"; + public static final String DEVICE_OUT_FM_NAME = "fm"; + public static final String DEVICE_OUT_FM_TX_NAME = "fm_tx"; public static String getDeviceName(int device) { @@ -365,6 +406,10 @@ public static String getDeviceName(int device) return DEVICE_OUT_ANC_HEADPHONE_NAME; case DEVICE_OUT_PROXY: return DEVICE_OUT_PROXY_NAME; + case DEVICE_OUT_FM: + return DEVICE_OUT_FM_NAME; + case DEVICE_OUT_FM_TX: + return DEVICE_OUT_FM_TX_NAME; case DEVICE_OUT_DEFAULT: default: return ""; diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index fda8c1bd2eded..11f0004ec5795 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -208,4 +208,6 @@ interface IAudioService { AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); boolean isCameraSoundForced(); + + void setParameters(String keyValuePairs); } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 0d78fe8832183..06d9109293f6a 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -179,6 +179,18 @@ private AudioSource() {} * is applied. */ public static final int VOICE_COMMUNICATION = 7; + + /** + * @hide + * Audio source for remote submix. + */ + public static final int REMOTE_SUBMIX_SOURCE = 8; + + /** @hide */ + public static final int FM_RX = 9; + + /** @hide */ + public static final int FM_RX_A2DP = 10; } /** @@ -307,7 +319,10 @@ public native void setAudioSource(int audio_source) * @see android.media.MediaRecorder.AudioSource */ public static final int getAudioSourceMax() { - return AudioSource.VOICE_COMMUNICATION; + // FIXME disable selection of the remote submxi source selection once test code + // doesn't rely on it + return AudioSource.FM_RX_A2DP; + //return AudioSource.VOICE_COMMUNICATION; } /** @@ -342,7 +357,7 @@ public void setProfile(CamcorderProfile profile) { profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) { // Nothing needs to be done. Call to setCaptureRate() enables // time lapse video recording. - } else { + } else if (profile.audioCodec >= 0) { setAudioEncodingBitRate(profile.audioBitRate); setAudioChannels(profile.audioChannels); setAudioSamplingRate(profile.audioSampleRate); @@ -697,6 +712,10 @@ public void prepare() throws IllegalStateException, IOException */ public native void start() throws IllegalStateException; +/** @hide +*/ + public native void pause() throws IllegalStateException; + /** * Stops recording. Call this after start(). Once recording is stopped, * you will have to configure it again as if it has just been constructed. diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java index 98258570738c5..e9b4b4d923238 100644 --- a/media/java/android/media/MiniThumbFile.java +++ b/media/java/android/media/MiniThumbFile.java @@ -394,7 +394,11 @@ public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic) RandomAccessFile r = miniThumbDataFile(); if (r == null) return null; - long pos = id * BYTES_PER_MINTHUMB; + long pos = getIndex(id, false); + if(pos < 0) return null; + + pos *= BYTES_PER_MINTHUMB; + FileLock lock = null; try { mBuffer.clear(); diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 98885911b86f4..eff4977d8e7fb 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -352,6 +352,14 @@ android_media_MediaRecorder_start(JNIEnv *env, jobject thiz) process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed."); } +static void +android_media_MediaRecorder_pause(JNIEnv *env, jobject thiz) +{ + ALOGV("pause"); + sp mr = getMediaRecorder(env, thiz); + process_media_recorder_call(env, mr->pause(), "java/lang/RuntimeException", "pause failed."); +} + static void android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz) { @@ -472,6 +480,7 @@ static JNINativeMethod gMethods[] = { {"_prepare", "()V", (void *)android_media_MediaRecorder_prepare}, {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, {"start", "()V", (void *)android_media_MediaRecorder_start}, + {"pause", "()V", (void *)android_media_MediaRecorder_pause}, {"stop", "()V", (void *)android_media_MediaRecorder_stop}, {"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset}, {"release", "()V", (void *)android_media_MediaRecorder_release}, diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 09a25d01741ae..18e6b09d36364 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -189,6 +189,7 @@ public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags ret.versionCode = pkg.versionCode; ret.installLocation = pkg.installLocation; ret.verifiers = pkg.verifiers; + ret.isTheme = pkg.isTheme; ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, packagePath, flags, threshold); diff --git a/packages/InputDevices/res/values-af/cm_strings.xml b/packages/InputDevices/res/values-af/cm_strings.xml new file mode 100644 index 0000000000000..d5ddbfcc7a37b --- /dev/null +++ b/packages/InputDevices/res/values-af/cm_strings.xml @@ -0,0 +1,35 @@ + + + Invoertoestelle + Android sleutelbord + Engels (VK) + Engels (VSA) + Engels (VSA), Dvorak styl + Duits + Frans + Frans (Kanada) + Russiese + Russiese, Mac styl + Spaans + Switserse Frans + Switserse Duits + Belgiese + Bulgaarse + Italiaanse + Deense + Noorweegse + Sweedse + Finse + Kroaties + Tsjeggiese + Estonian + Hongaarse + Yslandse + Portugees + Slovak + Slovenia + Turks + Oekraïens + Engels (VSA), Colemak styl + Armenian Oosterse + diff --git a/packages/InputDevices/res/values-ca/cm_strings.xml b/packages/InputDevices/res/values-ca/cm_strings.xml new file mode 100644 index 0000000000000..a0b6daefdce85 --- /dev/null +++ b/packages/InputDevices/res/values-ca/cm_strings.xml @@ -0,0 +1,20 @@ + + + + Anglès (EUA), estil Colemak + Armeni de l\'Est + diff --git a/packages/InputDevices/res/values-da/cm_strings.xml b/packages/InputDevices/res/values-da/cm_strings.xml new file mode 100644 index 0000000000000..8e40caba2547f --- /dev/null +++ b/packages/InputDevices/res/values-da/cm_strings.xml @@ -0,0 +1,50 @@ + + + + Inputenheder + Android-Tastatur + Engelsk (UK) + Engelsk (USA) + Engelsk (USA), Colemak + Engelsk (USA), Dvorak + Tysk + Fransk + Fransk (Canada) + Russisk + Russisk, Mac + Spansk + Fransk (Schweiz) + Tysk (Schweiz) + Belgisk + Bulgarsk + Italiensk + Dansk + Norsk + Svensk + Finsk + Kroatisk + Tjekkisk + Estisk + Ungarsk + Islandsk + Portugisisk + Slovakisk + Slovensk + Tyrkisk + Ukrainsk + Østarmensk + diff --git a/packages/InputDevices/res/values-lt/cm_strings.xml b/packages/InputDevices/res/values-lt/cm_strings.xml new file mode 100644 index 0000000000000..1ad774f35711d --- /dev/null +++ b/packages/InputDevices/res/values-lt/cm_strings.xml @@ -0,0 +1,50 @@ + + + + Įvesties įrenginiai + Klaviatura Android + Anglų (JK) + Anglų (JAV) + Anglų (JAV), Colemak stilius + Anglų (JAV), Dvorak stilius + Vokiečių + Prancūzų + Prancūzų (Kanada) + Rusų + Rusų, Mac stilius + Ispanų + Šveicarijos Prancūzų + Šveicarijos Vokiečių + Belgų + Bulgarų + Italų + Danų + Norvegų + Švedų + Suomių + Kroatų + Čekų + Estų + Vengrų + Islandų + Portugalų + Slovakų + Slovėnų + Turkų + Ukrainiečių + Armėnų + diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 930132812fecd..bd84a4387eda0 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -190,8 +190,9 @@ 2. Left --> 0 - + + 0 diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index a0167690eb28c..36ee99dad314f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +42,7 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -47,6 +51,7 @@ import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; @@ -1431,7 +1436,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { }; String[] secureToGlobal = { Settings.Global.PREFERRED_NETWORK_MODE, - Settings.Global.PREFERRED_CDMA_SUBSCRIPTION, + Settings.Global.CDMA_SUBSCRIPTION_MODE, }; moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, systemToGlobal, true); @@ -2028,8 +2033,14 @@ private void loadSystemSettings(SQLiteDatabase db) { loadIntegerSetting(stmt, Settings.System.QS_QUICK_PULLDOWN, R.integer.def_qs_quick_pulldown); - loadStringSetting(stmt, Settings.System.LOCKSCREEN_TARGETS, - R.string.def_lockscreen_targets); + if (mContext.getResources() + .getBoolean(com.android.internal.R.bool.config_voice_capable)) { + loadStringSetting(stmt, Settings.System.LOCKSCREEN_TARGETS, + R.string.def_lockscreen_targets); + } else { + loadStringSetting(stmt, Settings.System.LOCKSCREEN_TARGETS, + R.string.def_lockscreen_targets_no_telephony); + } loadIntegerSetting(stmt, Settings.System.UI_FORCE_OVERFLOW_BUTTON, R.integer.def_force_overflow_button); @@ -2300,9 +2311,24 @@ private void loadGlobalSettings(SQLiteDatabase db) { // Set the preferred network mode to 0 = Global, CDMA default int type; + if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) { + type = Phone.NT_MODE_GLOBAL; + } else { type = SystemProperties.getInt("ro.telephony.default_network", RILConstants.PREFERRED_NETWORK_MODE); - loadSetting(stmt, Settings.Global.PREFERRED_NETWORK_MODE, type); + } + + String val = Integer.toString(type); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + val = type + "," + type; + } + loadSetting(stmt, Settings.Global.PREFERRED_NETWORK_MODE, val); + + // Set the preferred cdma subscription source to target desired value or default + // value defined in CdmaSubscriptionSourceManager + type = SystemProperties.getInt("ro.telephony.default_cdma_sub", + CdmaSubscriptionSourceManager.PREFERRED_CDMA_SUBSCRIPTION); + loadSetting(stmt, Settings.Global.CDMA_SUBSCRIPTION_MODE, type); // --- New global settings start here } finally { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index ffb4c2026291c..63fdd094761b7 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -69,6 +69,7 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/SystemUI/res/layout/msim_signal_cluster_view.xml b/packages/SystemUI/res/layout/msim_signal_cluster_view.xml new file mode 100644 index 0000000000000..a09c0d1bae8c8 --- /dev/null +++ b/packages/SystemUI/res/layout/msim_signal_cluster_view.xml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/msim_status_bar.xml b/packages/SystemUI/res/layout/msim_status_bar.xml new file mode 100644 index 0000000000000..937ace6d516e1 --- /dev/null +++ b/packages/SystemUI/res/layout/msim_status_bar.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/msim_super_status_bar.xml b/packages/SystemUI/res/layout/msim_super_status_bar.xml new file mode 100644 index 0000000000000..77234bb2d7d39 --- /dev/null +++ b/packages/SystemUI/res/layout/msim_super_status_bar.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index 4511ea11e7339..e05cf86f8a4c5 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -39,7 +39,7 @@ android:clipToPadding="false" android:id="@+id/container" > - + diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml index 673d5a4db4e4e..8527ab6e31579 100644 --- a/packages/SystemUI/res/layout/signal_cluster_view.xml +++ b/packages/SystemUI/res/layout/signal_cluster_view.xml @@ -3,6 +3,8 @@ /* apps/common/assets/default/default/skins/StatusBar.xml ** ** Copyright 2011, The Android Open Source Project +** Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. +** Not a Contribution. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -102,6 +104,11 @@ android:layout_width="wrap_content" android:layout_gravity="end|bottom" /> + diff --git a/packages/SystemUI/res/values-af/cm_strings.xml b/packages/SystemUI/res/values-af/cm_strings.xml index d52cb676f41ba..d6f39ac6ce628 100644 --- a/packages/SystemUI/res/values-af/cm_strings.xml +++ b/packages/SystemUI/res/values-af/cm_strings.xml @@ -46,6 +46,8 @@ LTE af Volume Kamera + Gedeaktiveer + Aktiveer Kies \'n aksie om toe te ken Tuis-knoppie Onlangse programme knoppie diff --git a/packages/SystemUI/res/values-ca/cm_strings.xml b/packages/SystemUI/res/values-ca/cm_strings.xml index cfc525dbe9164..72e835c237bcf 100644 --- a/packages/SystemUI/res/values-ca/cm_strings.xml +++ b/packages/SystemUI/res/values-ca/cm_strings.xml @@ -55,6 +55,8 @@ LTE apagat Volum Càmera + Desactivat + Activat Extés Normal No es pot connectar a la càmera diff --git a/packages/SystemUI/res/values-cs/cm_strings.xml b/packages/SystemUI/res/values-cs/cm_strings.xml index 6f8114ad0acea..0544e582933df 100644 --- a/packages/SystemUI/res/values-cs/cm_strings.xml +++ b/packages/SystemUI/res/values-cs/cm_strings.xml @@ -48,6 +48,8 @@ LTE vyp. Hlasitost Fotoaparát + Zakázáno + Povoleno Rozšířený Normální Fotoaparát nelze spustit diff --git a/packages/SystemUI/res/values-da/cm_strings.xml b/packages/SystemUI/res/values-da/cm_strings.xml index 98f3056c7eaf8..684e12644fde5 100644 --- a/packages/SystemUI/res/values-da/cm_strings.xml +++ b/packages/SystemUI/res/values-da/cm_strings.xml @@ -53,6 +53,8 @@ LTE fra Lydstyrke Kamera + Deaktiveret + Aktiveret Udvidet Normal Kan ikke forbinde til kameraet diff --git a/packages/SystemUI/res/values-it/cm_strings.xml b/packages/SystemUI/res/values-it/cm_strings.xml index dbb5752dbe1f1..e4aa9c371c919 100644 --- a/packages/SystemUI/res/values-it/cm_strings.xml +++ b/packages/SystemUI/res/values-it/cm_strings.xml @@ -25,7 +25,7 @@ Timeout impostato a: %1$d %2$s GPS GPS OFF - Blocca telefono + Sospendi dispositivo Suoneria ON Suoneria OFF Vibrazione ON diff --git a/packages/SystemUI/res/values-lt/cm_strings.xml b/packages/SystemUI/res/values-lt/cm_strings.xml new file mode 100644 index 0000000000000..693c900dde718 --- /dev/null +++ b/packages/SystemUI/res/values-lt/cm_strings.xml @@ -0,0 +1,76 @@ + + + + HSPA+ + DCHSPAP + Ekrano delsimas po: %1$d %2$s + GPS + GPS išjungta + Užmigti + Garsas įjungtas + Garsas išjungtas + Vibracija įjungta + Vibracija išjungta + Įjungti + Išjungti + Garsas + Užrakinti ekraną + Tinklo režimas + Išjungta + Įjungta + Pranešti apie klaidą + Sinchronizuoti + Sinchronizavimas išjungtas + Žibintuvėlis + Žibintuvėlis išjungtas + NFC + NFC išjungtas + Ekrano delsimo laikas + Atjungtas + Pririšimas išjungtas + Pririšimas + Profilis + Wi-Fi AP + Wi-Fi AP išjungtas + Tylos laikas + Tylos laikas išjungtas + LTE + LTE išjungtas + Garsas + Kamera + Pasirinkite veiksmą norėdami jį priskirti + Pagrindinis mygtukas + \'Naujų\' mygtukas + Paieškos mygtukas + Mygtukas atgal + Tuščias mygtukas + Meniu mygtukas (automatiškai slepiamas) + Meniu mygtukas (visada rodomas) + Meniu mygtukas + Įkraunama (%d%%) + Įkrauta + %d%% Likę + Nėra ryšio + Lėktuvo režimas įjungtas + Tik pagalbos skambučiai + Išplėstas + Normalus + Nepavyksta prisijungti prie kameros + Garsas %s. + Vibracija %s. + Naudoti Cido atvaizdą + Naudokite Cido atvaizdą vietoi Dražės svajonėje + diff --git a/packages/SystemUI/res/values-nl/cm_strings.xml b/packages/SystemUI/res/values-nl/cm_strings.xml index 8e9f1be210cd4..4cd3c740a8144 100644 --- a/packages/SystemUI/res/values-nl/cm_strings.xml +++ b/packages/SystemUI/res/values-nl/cm_strings.xml @@ -35,6 +35,8 @@ LTE uit Volume Camera + Uitgeschakeld + Ingeschakeld Uitgebreid Normaal Kan geen verbinding maken met de camera. diff --git a/packages/SystemUI/res/values-pl/cm_strings.xml b/packages/SystemUI/res/values-pl/cm_strings.xml index 65eae6774aba5..d1cdf6794b0a1 100644 --- a/packages/SystemUI/res/values-pl/cm_strings.xml +++ b/packages/SystemUI/res/values-pl/cm_strings.xml @@ -49,6 +49,8 @@ LTE wył. Głośność Kamera + ADB wył. + ADB Wybierz akcję do przypisania Przycisk Home Przycisk Ostatnie aplikacje diff --git a/packages/SystemUI/res/values-ro/cm_strings.xml b/packages/SystemUI/res/values-ro/cm_strings.xml index c9abb211ed777..f027e4b075113 100644 --- a/packages/SystemUI/res/values-ro/cm_strings.xml +++ b/packages/SystemUI/res/values-ro/cm_strings.xml @@ -14,17 +14,63 @@ limitations under the License. --> - "GPS" - "Adormire" - "Sunet activat" - "Sunet dezactivat" - "Vibrare activată" - "Vibrare dezactivată" - "Activat" - "Dezactivat" - "Sunet" - "Ecran Blocare" - "Mod Reţea" - "Raport Erori" + HSPA+ + DCHSPAP + Sunet%s. + Vibrare%s. + Folosește capul lui Cid + Folosește capul lui Cid in locul bomboanelor gumate înăuntrul visului + Profil + Inactivitate ecran setată la: %1$d %2$s + GPS + GPS + Stinge ecran + Sunet + Sunet + Vibrare + Vibrare + Pornit + Oprit + Sunet + Blocare ecran + Mod rețea + Raport erori Sincronizare - + Sincronizare + Lanternă + Lanternă + NFC + NFC + Inactivitate ecran + Deconectat + USB Tether + USB Tether + Hotspot Wi-Fi + Hotspot Wi-Fi + Ore de liniște + Ore de liniște + LTE + LTE + Volum + Cameră + ADB în rețea + ADB în rețea + Extins + Normal + Nu se poate conecta la cameră + Atribuie o acțiune + Buttonul Acasă + Buttonul Recent + Buttonul Caută + Buttonul Înapoi + Button Gol + Butonul Meniu (auto ascunde) + Butonul Meniu (arată mereu) + Butonul Meniu + Încarcă (%d%%) + Încărcat + %d%% rămas + Fără serviciu + Mod avion pornit + Doar apeluri de urgență + \ No newline at end of file diff --git a/packages/SystemUI/res/values-ru/cm_strings.xml b/packages/SystemUI/res/values-ru/cm_strings.xml index 7d486e402dd67..f5ea5c654caed 100644 --- a/packages/SystemUI/res/values-ru/cm_strings.xml +++ b/packages/SystemUI/res/values-ru/cm_strings.xml @@ -21,7 +21,7 @@ Заряжено Профиль GPS выкл. - Сон + Выключить экран Вкл. звук Выкл. звук Вкл. вибро @@ -30,7 +30,7 @@ Выкл. Звук Экран блокировки - Тип сети + Режим сети Отчёт об ошибке Синхронизация Синхронизация выкл. @@ -48,8 +48,8 @@ LTE выкл. Громкость Камера - ADB по сети выкл. - ADB по сети + Отладка выкл. + Отладка по сети Расширенный Обычный Выберите действие diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index d1a9ecd7fadd5..11b6c71a183bf 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -49,8 +49,8 @@ "Общий модем доступен через Bluetooth" "Настройка способов ввода" "Физическая клавиатура" - "Открыть приложению \"%1$s\" доступ к USB-устройству?" - "Открыть приложению \"%1$s\" доступ к USB-устройству?" + "Открыть приложению «%1$s» доступ к USB-устройству?" + "Открыть приложению «%1$s» доступ к USB-устройству?" "Запускать %1$s при подключении этого USB-устройства?" "Запускать %1$s при подключении этого USB-аксессуара?" "Приложения не поддерживают это USB-устройство. Подробнее о нем читайте здесь: %1$s." @@ -146,7 +146,7 @@ "Телетайп включен." "Вибровызов." "Беззвучный режим." - "Приложение \"%s\" удалено из списка." + "Приложение «%s» удалено из списка." "Уведомление закрыто" "Панель уведомлений" "Быстрые настройки" diff --git a/packages/SystemUI/res/values-sk/cm_strings.xml b/packages/SystemUI/res/values-sk/cm_strings.xml index f3f22986d53d1..a97adb13e02a3 100644 --- a/packages/SystemUI/res/values-sk/cm_strings.xml +++ b/packages/SystemUI/res/values-sk/cm_strings.xml @@ -18,13 +18,13 @@ HSPA+ Zvuk %s. Vibrácie %s. - Použiť hlavičku Cid - Použije hlavičku Cid namiesto vnútornej hlavičky jellybean + Použiť hlavičku Cida + Použiť hlavičku Cida namiesto sladkých želé fazulí Profil Časový limit obrazovky bol nastavený na: %1$d %2$s GPS GPS vyp. - Go to sleep + Prejsť do spánku Zvuk zap. Zvuk vyp. Vibrácie zap. @@ -37,14 +37,14 @@ Nahlásiť chybu Synch. Synch. vyp. - Torch - Torch vyp. + Svietidlo + Svietidlo vyp. NFC NFC vyp. Časový limit Odpojené - Tethering off - Tethering + Zdieľanie vyp. + Zdieľanie Wi-Fi AP Wi-Fi AP vyp. Čas ticha @@ -56,19 +56,23 @@ Normálna Vyberte akciu na priradenie Fotoaparát + Zakázané + Povolené Nemožno sa pripojiť k fotoaparátu - Tlačidlo domov - Tlačidlo nedávne - Tlačidlo hľadať - Tlačidlo späť + Tlačidlo Domov + Tlačidlo Nedávne + Tlačidlo Hľadať + Tlačidlo Späť Prázdne tlačidlo - Tlačidlo ponuky (autom. skrývané) - Tlačidlo ponuky (stále viditeľné) - Tlačidlo ponuky + Tlačidlo Ponuka (autom. skrývané) + Tlačidlo Ponuka (stále viditeľné) + Tlačidlo Ponuka Nabíjanie (%d%%) Nabitá %d%% zostáva Žiadna služba - Režim v lietadle zap. + Režim V lietadle zap. Iba tiesňové volania + Batéria %d%%. + DCHSPAP diff --git a/packages/SystemUI/res/values-sv/cm_strings.xml b/packages/SystemUI/res/values-sv/cm_strings.xml index fa57f66f7ad37..cc8c77351775e 100644 --- a/packages/SystemUI/res/values-sv/cm_strings.xml +++ b/packages/SystemUI/res/values-sv/cm_strings.xml @@ -51,6 +51,8 @@ Expanderat Normalt Kan inte kontakta kamera + Inaktiverad + Aktiverad Välj åtgärd att tilldela Hemknapp Tidigareknapp diff --git a/packages/SystemUI/res/values-zh-rTW/cm_strings.xml b/packages/SystemUI/res/values-zh-rTW/cm_strings.xml index 9ba1dd26a92cc..2a373af9cdc38 100644 --- a/packages/SystemUI/res/values-zh-rTW/cm_strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/cm_strings.xml @@ -1,5 +1,5 @@ - + + + + + + @drawable/ic_qs_perf_profile_pwrsv + @drawable/ic_qs_perf_profile_bal + @drawable/ic_qs_perf_profile_perf + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 77d0452bda7c3..abcf3a6af9291 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -220,7 +220,7 @@ 32dp - 2dip + 4dip -1dip diff --git a/packages/SystemUI/src/com/android/systemui/quicksettings/PerformanceProfileTile.java b/packages/SystemUI/src/com/android/systemui/quicksettings/PerformanceProfileTile.java new file mode 100644 index 0000000000000..d94dadbff737a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/quicksettings/PerformanceProfileTile.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 Sven Dawitz for the CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.quicksettings; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.net.Uri; +import android.provider.Settings; +import android.view.View; + +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.QuickSettingsController; + +public class PerformanceProfileTile extends QuickSettingsTile { + + private String[] mEntries; + private TypedArray mTypedArrayDrawables; + private int mCurrentValue; + + private String mPerfProfileDefaultEntry; + private String[] mPerfProfileValues; + + public PerformanceProfileTile(Context context, QuickSettingsController qsc) { + super(context, qsc); + + Resources res = context.getResources(); + mEntries = res.getStringArray(com.android.internal.R.array.perf_profile_entries); + mTypedArrayDrawables = res.obtainTypedArray(R.array.perf_profile_drawables); + + mPerfProfileDefaultEntry = res.getString( + com.android.internal.R.string.config_perf_profile_default_entry); + mPerfProfileValues = res.getStringArray(com.android.internal.R.array.perf_profile_values); + + updateCurrentValue(); + + // Register a callback to detect changes in system properties + qsc.registerObservedContent(Settings.System.getUriFor( + Settings.System.PERFORMANCE_PROFILE), this); + + mOnClick = new View.OnClickListener() { + @Override + public void onClick(View v) { + changeToNextProfile(); + } + }; + } + + @Override + void onPostCreate() { + updateTile(); + super.onPostCreate(); + } + + @Override + public void updateResources() { + updateTile(); + super.updateResources(); + } + + private void changeToNextProfile() { + int current = mCurrentValue + 1; + if (current >= mPerfProfileValues.length) { + current = 0; + } + Settings.System.putString(mContext.getContentResolver(), + Settings.System.PERFORMANCE_PROFILE, mPerfProfileValues[current]); + } + + private void updateCurrentValue() { + String perfProfile = Settings.System.getString(mContext.getContentResolver(), + Settings.System.PERFORMANCE_PROFILE); + if (perfProfile == null) { + perfProfile = mPerfProfileDefaultEntry; + } + + int count = mPerfProfileValues.length; + for (int i = 0; i < count; i++) { + if (mPerfProfileValues[i].equals(perfProfile)) { + mCurrentValue = i; + return; + } + } + + // Something was wrong + mCurrentValue = 0; + } + + private synchronized void updateTile() { + mDrawable = mTypedArrayDrawables.getResourceId(mCurrentValue, -1); + mLabel = mEntries[mCurrentValue]; + } + + + @Override + public void onReceive(Context context, Intent intent) { + updateCurrentValue(); + updateResources(); + } + + @Override + public void onChangeUri(ContentResolver resolver, Uri uri) { + updateCurrentValue(); + updateResources(); + } + +} + diff --git a/packages/SystemUI/src/com/android/systemui/quicksettings/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/quicksettings/RingerModeTile.java index d3d2b5ece5a79..549268aeefe95 100644 --- a/packages/SystemUI/src/com/android/systemui/quicksettings/RingerModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/quicksettings/RingerModeTile.java @@ -9,39 +9,35 @@ import android.os.Vibrator; import android.provider.Settings; import android.text.TextUtils; -import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnLongClickListener; import com.android.systemui.R; import com.android.systemui.statusbar.phone.QuickSettingsController; -import com.android.systemui.statusbar.phone.QuickSettingsContainerView; -public class RingerModeTile extends QuickSettingsTile { +import java.util.ArrayList; +public class RingerModeTile extends QuickSettingsTile { private static final String SEPARATOR = "OV=I=XseparatorX=I=VO"; - // Define the available ringer modes - private final Ringer mSilentRinger = new Ringer(AudioManager.RINGER_MODE_SILENT, false); - private final Ringer mVibrateRinger = new Ringer(AudioManager.RINGER_MODE_VIBRATE, true); - private final Ringer mSoundRinger = new Ringer(AudioManager.RINGER_MODE_NORMAL, false); - private final Ringer mSoundVibrateRinger = new Ringer(AudioManager.RINGER_MODE_NORMAL, true); - private final Ringer[] mRingers = new Ringer[] { - mSilentRinger, mVibrateRinger, mSoundRinger, mSoundVibrateRinger + private static final Ringer[] RINGERS = new Ringer[] { + new Ringer(AudioManager.RINGER_MODE_SILENT, false, R.drawable.ic_qs_ring_off), + new Ringer(AudioManager.RINGER_MODE_VIBRATE, true, R.drawable.ic_qs_vibrate_on), + new Ringer(AudioManager.RINGER_MODE_NORMAL, false, R.drawable.ic_qs_ring_on), + new Ringer(AudioManager.RINGER_MODE_NORMAL, true, R.drawable.ic_qs_ring_vibrate_on) }; - private int mRingersIndex; - private int[] mRingerValues = new int[] { - 0, 1, 2, 3 - }; - private int mRingerValuesIndex; + private ArrayList mRingers; + private int mRingerIndex; private AudioManager mAudioManager; + private Vibrator mVibrator; public RingerModeTile(Context context, QuickSettingsController qsc) { super(context, qsc); + mRingers = new ArrayList(); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); // Tile actions mOnClick = new View.OnClickListener() { @@ -52,7 +48,7 @@ public void onClick(View v) { } }; - mOnLongClick = new OnLongClickListener() { + mOnLongClick = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { startSettingsActivity(android.provider.Settings.ACTION_SOUND_SETTINGS); @@ -60,10 +56,10 @@ public boolean onLongClick(View v) { } }; qsc.registerAction(AudioManager.RINGER_MODE_CHANGED_ACTION, this); - qsc.registerObservedContent(Settings.System.getUriFor(Settings.System.EXPANDED_RING_MODE) - , this); - qsc.registerObservedContent(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING) - , this); + qsc.registerObservedContent( + Settings.System.getUriFor(Settings.System.EXPANDED_RING_MODE), this); + qsc.registerObservedContent( + Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING), this); } @Override @@ -73,14 +69,14 @@ public void onReceive(Context context, Intent intent) { @Override public void onChangeUri(ContentResolver resolver, Uri uri) { - updateSettings(mContext.getContentResolver()); + updateSettings(); updateResources(); } @Override void onPostCreate() { // Load the available ringer modes - updateSettings(mContext.getContentResolver()); + updateSettings(); // Make sure we show the initial state correctly updateTile(); @@ -94,125 +90,97 @@ public void updateResources() { super.updateResources(); } - private synchronized void updateTile() { + private void updateTile() { // The title does not change mLabel = mContext.getString(R.string.quick_settings_ringer_normal); // The icon will change depending on index findCurrentState(); - switch (mRingersIndex) { - case 0: - mDrawable = R.drawable.ic_qs_ring_off; - break; - case 1: - mDrawable = R.drawable.ic_qs_vibrate_on; - break; - case 2: - mDrawable = R.drawable.ic_qs_ring_on; - break; - case 3: - mDrawable = R.drawable.ic_qs_ring_vibrate_on; - break; - } - - for (int i = 0; i < mRingerValues.length; i++) { - if (mRingersIndex == mRingerValues[i]) { - mRingerValuesIndex = i; - break; - } - } + mDrawable = mRingers.get(mRingerIndex).mDrawable; } protected void toggleState() { - mRingerValuesIndex++; - if (mRingerValuesIndex > mRingerValues.length - 1) { - mRingerValuesIndex = 0; + mRingerIndex++; + if (mRingerIndex >= mRingers.size()) { + mRingerIndex = 0; } - mRingersIndex = mRingerValues[mRingerValuesIndex]; - if (mRingersIndex > mRingers.length - 1) { - mRingersIndex = 0; + Ringer r = mRingers.get(mRingerIndex); + + // If we are setting a vibrating state, vibrate to indicate it + if (r.mVibrateWhenRinging) { + mVibrator.vibrate(250); } - Ringer ringer = mRingers[mRingersIndex]; - ringer.execute(mContext); + // Set the desired state + ContentResolver resolver = mContext.getContentResolver(); + Settings.System.putIntForUser(resolver, Settings.System.VIBRATE_WHEN_RINGING, + r.mVibrateWhenRinging ? 1 : 0, UserHandle.USER_CURRENT); + mAudioManager.setRingerMode(r.mRingerMode); } public String[] parseStoredValue(CharSequence val) { if (TextUtils.isEmpty(val)) { - return null; - } else { - return val.toString().split(SEPARATOR); + return null; } + return val.toString().split(SEPARATOR); } - private void updateSettings(ContentResolver resolver) { - String[] modes = parseStoredValue(Settings.System.getStringForUser( - resolver, Settings.System.EXPANDED_RING_MODE, UserHandle.USER_CURRENT)); + private void updateSettings() { + String setting = Settings.System.getStringForUser(mContext.getContentResolver(), + Settings.System.EXPANDED_RING_MODE, UserHandle.USER_CURRENT); + String[] modes = parseStoredValue(setting); + Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); + boolean hasVibrator = vibrator.hasVibrator(); + + mRingers.clear(); + if (modes == null || modes.length == 0) { - mRingerValues = new int[] { - 0, 1, 2, 3 - }; + for (Ringer r : RINGERS) { + if (hasVibrator || !r.mVibrateWhenRinging) { + mRingers.add(r); + } + } } else { - mRingerValues = new int[modes.length]; for (int i = 0; i < modes.length; i++) { - mRingerValues[i] = Integer.valueOf(modes[i]); + int index = Integer.valueOf(modes[i]); + Ringer r = index < RINGERS.length ? RINGERS[index] : null; + + if (r != null && (hasVibrator || !r.mVibrateWhenRinging)) { + mRingers.add(r); + } } } + if (mRingers.isEmpty()) { + mRingers.add(RINGERS[0]); + } } private void findCurrentState() { - ContentResolver resolver = mContext.getContentResolver(); - boolean vibrateWhenRinging = Settings.System.getIntForUser(resolver, + boolean vibrateWhenRinging = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0, UserHandle.USER_CURRENT) == 1; int ringerMode = mAudioManager.getRingerMode(); - Ringer ringer = new Ringer(ringerMode, vibrateWhenRinging); - for (int i = 0; i < mRingers.length; i++) { - if (mRingers[i].equals(ringer)) { - mRingersIndex = i; + mRingerIndex = 0; + + for (int i = 0; i < mRingers.size(); i++) { + Ringer r = mRingers.get(i); + if (ringerMode == r.mRingerMode && vibrateWhenRinging == r.mVibrateWhenRinging) { + mRingerIndex = i; break; } } } - private class Ringer { + private static class Ringer { final boolean mVibrateWhenRinging; final int mRingerMode; + final int mDrawable; - Ringer( int ringerMode, boolean vibrateWhenRinging) { + Ringer(int ringerMode, boolean vibrateWhenRinging, int drawable) { mVibrateWhenRinging = vibrateWhenRinging; mRingerMode = ringerMode; - } - - void execute(Context context) { - // If we are setting a vibrating state, vibrate to indicate it - if (mVibrateWhenRinging) { - Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); - vibrator.vibrate(250); - } - - // Set the desired state - ContentResolver resolver = context.getContentResolver(); - Settings.System.putIntForUser(resolver, Settings.System.VIBRATE_WHEN_RINGING, - mVibrateWhenRinging ? 1 : 0, UserHandle.USER_CURRENT); - mAudioManager.setRingerMode(mRingerMode); - } - - @Override - public boolean equals(Object o) { - if (o == null) { - return false; - } - if (o.getClass() != getClass()) { - return false; - } - Ringer r = (Ringer) o; - if ((mRingerMode == AudioManager.RINGER_MODE_SILENT || mRingerMode == AudioManager.RINGER_MODE_VIBRATE) - && (r.mRingerMode == mRingerMode)) - return true; - return r.mVibrateWhenRinging == mVibrateWhenRinging - && r.mRingerMode == mRingerMode; + mDrawable = drawable; } } } diff --git a/packages/SystemUI/src/com/android/systemui/quicksettings/UserTile.java b/packages/SystemUI/src/com/android/systemui/quicksettings/UserTile.java index 1bcbe459f1b53..30bd44f787bec 100644 --- a/packages/SystemUI/src/com/android/systemui/quicksettings/UserTile.java +++ b/packages/SystemUI/src/com/android/systemui/quicksettings/UserTile.java @@ -1,5 +1,9 @@ package com.android.systemui.quicksettings; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + import android.app.ActivityManagerNative; import android.content.Context; import android.content.Intent; @@ -7,13 +11,17 @@ import android.content.pm.UserInfo; import android.database.Cursor; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.AsyncTask; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Profile; import android.util.Log; @@ -31,6 +39,7 @@ public class UserTile extends QuickSettingsTile { private static final String TAG = "UserTile"; + private static final String INTENT_EXTRA_NEW_LOCAL_PROFILE = "newLocalProfile"; private Drawable userAvatar; private AsyncTask> mUserInfoTask; @@ -43,16 +52,26 @@ public void onClick(View v) { mQsc.mBar.collapseAllPanels(true); final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (um.getUsers(true).size() > 1) { + int numUsers = um.getUsers(true).size(); + if (numUsers <= 1) { + final Cursor cursor = mContext.getContentResolver().query( + Profile.CONTENT_URI, null, null, null, null); + if (cursor.moveToNext() && !cursor.isNull(0)) { + Intent intent = new Intent(Intent.ACTION_VIEW, ContactsContract.Profile.CONTENT_URI); + startSettingsActivity(intent); + } else { + Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); + intent.putExtra(INTENT_EXTRA_NEW_LOCAL_PROFILE, true); + startSettingsActivity(intent); + } + cursor.close(); + } else { try { WindowManagerGlobal.getWindowManagerService().lockNow( null); } catch (RemoteException e) { Log.e(TAG, "Couldn't show user switcher", e); } - } else { - Intent intent = new Intent(Intent.ACTION_VIEW, ContactsContract.Profile.CONTENT_URI); - startSettingsActivity(intent); } } }; @@ -113,17 +132,9 @@ protected Pair doInBackground(Void... params) { final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - // Fall back to the UserManager nickname if we can't read the name from the local - // profile below. - String name = userName; + String name = null; Drawable avatar = null; - Bitmap rawAvatar = um.getUserIcon(userId); - if (rawAvatar != null) { - avatar = new BitmapDrawable(mContext.getResources(), rawAvatar); - } else { - avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); - } - + String id = null; // If it's a single-user device, get the profile name, since the nickname is not // usually valid if (um.getUsers().size() <= 1) { @@ -135,10 +146,37 @@ protected Pair doInBackground(Void... params) { try { if (cursor.moveToFirst()) { name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); + id = cursor.getString(cursor.getColumnIndex(Phone._ID)); } } finally { cursor.close(); } + // Fall back to the UserManager nickname if we can't read the name from the local + // profile below. + if (name == null) { + avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); + name = mContext.getResources().getString(com.android.internal.R.string.owner_name); + } else { + Bitmap rawAvatar = null; + InputStream is = null; + try { + Uri.Builder uriBuilder = ContactsContract.RawContacts.CONTENT_URI.buildUpon(); + uriBuilder.appendPath(id); + uriBuilder.appendPath(Contacts.Photo.DISPLAY_PHOTO); + is = mContext.getContentResolver().openInputStream(uriBuilder.build()); + rawAvatar = BitmapFactory.decodeStream(is); + avatar = new BitmapDrawable(mContext.getResources(), rawAvatar); + } catch (FileNotFoundException e) { + avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + } } } return new Pair(name, avatar); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MSimSignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/MSimSignalClusterView.java new file mode 100644 index 0000000000000..7a845bddf2594 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MSimSignalClusterView.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.telephony.MSimTelephonyManager; +import android.telephony.TelephonyManager; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.accessibility.AccessibilityEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.android.internal.telephony.MSimConstants; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.MSimNetworkController; + +import com.android.systemui.R; + +// Intimately tied to the design of res/layout/msim_signal_cluster_view.xml +public class MSimSignalClusterView + extends LinearLayout + implements MSimNetworkController.MSimSignalCluster { + + static final boolean DEBUG = false; + static final String TAG = "MSimSignalClusterView"; + + MSimNetworkController mMSimNC; + + private boolean mWifiVisible = false; + private int mWifiStrengthId = 0, mWifiActivityId = 0; + private boolean mMobileVisible = false; + private int[] mMobileStrengthId; + private int[] mMobileActivityId; + private int[] mMobileTypeId; + private int[] mNoSimIconId; + private boolean mIsAirplaneMode = false; + private int mAirplaneIconId = 0; + private String mWifiDescription, mMobileTypeDescription; + private String[] mMobileDescription; + + ViewGroup mWifiGroup; + ViewGroup[] mMobileGroup; + ImageView mWifi, mWifiActivity, mAirplane; + ImageView[] mNoSimSlot; + ImageView[] mMobile; + ImageView[] mMobileActivity; + ImageView[] mMobileType; + View mSpacer; + private int[] mMobileGroupResourceId = {R.id.mobile_combo, R.id.mobile_combo_sub2, + R.id.mobile_combo_sub3}; + private int[] mMobileResourceId = {R.id.mobile_signal, R.id.mobile_signal_sub2, + R.id.mobile_signal_sub3}; + private int[] mMobileActResourceId = {R.id.mobile_inout, R.id.mobile_inout_sub2, + R.id.mobile_inout_sub3}; + private int[] mMobileTypeResourceId = {R.id.mobile_type, R.id.mobile_type_sub2, + R.id.mobile_type_sub3}; + private int[] mNoSimSlotResourceId = {R.id.no_sim, R.id.no_sim_slot2, R.id.no_sim_slot3}; + private int mNumPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + + public MSimSignalClusterView(Context context) { + this(context, null); + } + + public MSimSignalClusterView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MSimSignalClusterView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mMobileStrengthId = new int[mNumPhones]; + mMobileDescription = new String[mNumPhones]; + mMobileTypeId = new int[mNumPhones]; + mMobileActivityId = new int[mNumPhones]; + mNoSimIconId = new int[mNumPhones]; + mMobileGroup = new ViewGroup[mNumPhones]; + mNoSimSlot = new ImageView[mNumPhones]; + mMobile = new ImageView[mNumPhones]; + mMobileActivity = new ImageView[mNumPhones]; + mMobileType = new ImageView[mNumPhones]; + for(int i=0; i < mNumPhones; i++) { + mMobileStrengthId[i] = 0; + mMobileTypeId[i] = 0; + mMobileActivityId[i] = 0; + mNoSimIconId[i] = 0; + } + } + + public void setNetworkController(MSimNetworkController nc) { + if (DEBUG) Slog.d(TAG, "MSimNetworkController=" + nc); + mMSimNC = nc; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo); + mWifi = (ImageView) findViewById(R.id.wifi_signal); + mWifiActivity = (ImageView) findViewById(R.id.wifi_inout); + mSpacer = findViewById(R.id.spacer); + mAirplane = (ImageView) findViewById(R.id.airplane); + + for (int i = 0; i < mNumPhones; i++) { + mMobileGroup[i] = (ViewGroup) findViewById(mMobileGroupResourceId[i]); + mMobile[i] = (ImageView) findViewById(mMobileResourceId[i]); + mMobileActivity[i] = (ImageView) findViewById(mMobileActResourceId[i]); + mMobileType[i] = (ImageView) findViewById(mMobileTypeResourceId[i]); + mNoSimSlot[i] = (ImageView) findViewById(mNoSimSlotResourceId[i]); + } + applySubscription(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + @Override + protected void onDetachedFromWindow() { + mWifiGroup = null; + mWifi = null; + mWifiActivity = null; + mSpacer = null; + mAirplane = null; + for (int i = 0; i < mNumPhones; i++) { + mMobileGroup[i] = null; + mMobile[i] = null; + mMobileActivity[i] = null; + mMobileType[i] = null; + mNoSimSlot[i] = null; + } + super.onDetachedFromWindow(); + } + + @Override + public void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon, + String contentDescription) { + mWifiVisible = visible; + mWifiStrengthId = strengthIcon; + mWifiActivityId = activityIcon; + mWifiDescription = contentDescription; + + applySubscription(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + @Override + public void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon, + int typeIcon, String contentDescription, String typeContentDescription, + int noSimIcon, int subscription) { + mMobileVisible = visible; + mMobileStrengthId[subscription] = strengthIcon; + mMobileActivityId[subscription] = activityIcon; + mMobileTypeId[subscription] = typeIcon; + mMobileDescription[subscription] = contentDescription; + mMobileTypeDescription = typeContentDescription; + mNoSimIconId[subscription] = noSimIcon; + + applySubscription(subscription); + } + + @Override + public void setIsAirplaneMode(boolean is, int airplaneIconId) { + mIsAirplaneMode = is; + mAirplaneIconId = airplaneIconId; + + applySubscription(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + // Standard group layout onPopulateAccessibilityEvent() implementations + // ignore content description, so populate manually + if (mWifiVisible && mWifiGroup.getContentDescription() != null) + event.getText().add(mWifiGroup.getContentDescription()); + if (mMobileVisible && mMobileGroup[MSimConstants.DEFAULT_SUBSCRIPTION]. + getContentDescription() != null) + event.getText().add(mMobileGroup[MSimConstants.DEFAULT_SUBSCRIPTION]. + getContentDescription()); + return super.dispatchPopulateAccessibilityEvent(event); + } + + // Run after each indicator change. + private void applySubscription(int subscription) { + if (mWifiGroup == null) return; + + if (mWifiVisible) { + mWifiGroup.setVisibility(View.VISIBLE); + mWifi.setImageResource(mWifiStrengthId); + mWifiActivity.setImageResource(mWifiActivityId); + mWifiGroup.setContentDescription(mWifiDescription); + } else { + mWifiGroup.setVisibility(View.GONE); + } + + if (DEBUG) Slog.d(TAG, + String.format("wifi: %s sig=%d act=%d", + (mWifiVisible ? "VISIBLE" : "GONE"), mWifiStrengthId, mWifiActivityId)); + + if (mMobileVisible && !mIsAirplaneMode) { + mMobileGroup[subscription].setVisibility(View.VISIBLE); + mMobile[subscription].setImageResource(mMobileStrengthId[subscription]); + mMobileGroup[subscription].setContentDescription(mMobileTypeDescription + " " + + mMobileDescription[subscription]); + mMobileActivity[subscription].setImageResource(mMobileActivityId[subscription]); + mMobileType[subscription].setImageResource(mMobileTypeId[subscription]); + mMobileType[subscription].setVisibility( + !mWifiVisible ? View.VISIBLE : View.GONE); + mNoSimSlot[subscription].setImageResource(mNoSimIconId[subscription]); + } else { + mMobileGroup[subscription].setVisibility(View.GONE); + } + + if (mIsAirplaneMode) { + mAirplane.setVisibility(View.VISIBLE); + mAirplane.setImageResource(mAirplaneIconId); + } else { + mAirplane.setVisibility(View.GONE); + } + + if (subscription != 0) { + if (mMobileVisible && mWifiVisible && ((mIsAirplaneMode) || + (mNoSimIconId[subscription] != 0))) { + mSpacer.setVisibility(View.INVISIBLE); + } else { + mSpacer.setVisibility(View.GONE); + } + } + + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 22b1112f8cb64..723a1c8b9ff72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2011 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +54,8 @@ public class SignalClusterView private boolean mWifiVisible = false; private int mWifiStrengthId = 0, mWifiActivityId = 0; private boolean mMobileVisible = false; - private int mMobileStrengthId = 0, mMobileActivityId = 0, mMobileTypeId = 0; + private int mMobileStrengthId = 0, mMobileActivityId = 0; + private int mMobileTypeId = 0, mNoSimIconId = 0; private boolean mIsAirplaneMode = false; private int mAirplaneIconId = 0; private boolean mEtherVisible = false; @@ -59,7 +63,7 @@ public class SignalClusterView private String mWifiDescription, mMobileDescription, mMobileTypeDescription, mEtherDescription; ViewGroup mWifiGroup, mMobileGroup; - ImageView mWifi, mMobile, mWifiActivity, mMobileActivity, mMobileType, mAirplane, mEther; + ImageView mWifi, mMobile, mWifiActivity, mMobileActivity, mMobileType, mAirplane, mEther, mNoSimSlot; View mSpacer; Handler mHandler; @@ -119,6 +123,7 @@ protected void onAttachedToWindow() { mMobile = (ImageView) findViewById(R.id.mobile_signal); mMobileActivity = (ImageView) findViewById(R.id.mobile_inout); mMobileType = (ImageView) findViewById(R.id.mobile_type); + mNoSimSlot = (ImageView) findViewById(R.id.no_sim); mSpacer = findViewById(R.id.spacer); mAirplane = (ImageView) findViewById(R.id.airplane); mEther = (ImageView) findViewById(R.id.ethernet); @@ -137,6 +142,7 @@ protected void onDetachedFromWindow() { mMobile = null; mMobileActivity = null; mMobileType = null; + mNoSimSlot = null; mSpacer = null; mAirplane = null; mEther = null; @@ -157,13 +163,15 @@ public void setWifiIndicators(boolean visible, int strengthIcon, int activityIco @Override public void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon, - int typeIcon, String contentDescription, String typeContentDescription) { + int typeIcon, String contentDescription, String typeContentDescription, + int noSimIcon) { mMobileVisible = visible; mMobileStrengthId = strengthIcon; mMobileActivityId = activityIcon; mMobileTypeId = typeIcon; mMobileDescription = contentDescription; mMobileTypeDescription = typeContentDescription; + mNoSimIconId = noSimIcon; apply(); } @@ -250,6 +258,7 @@ private void apply() { mMobileGroup.setContentDescription(mMobileTypeDescription + " " + mMobileDescription); mMobileGroup.setVisibility(View.VISIBLE); + mNoSimSlot.setImageResource(mNoSimIconId); } else { mMobileGroup.setVisibility(View.GONE); } @@ -269,7 +278,8 @@ private void apply() { mEther.setVisibility(View.GONE); } - if (mMobileVisible && mWifiVisible && mIsAirplaneMode) { + if (mMobileVisible && mWifiVisible && + ((mIsAirplaneMode) || (mNoSimIconId != 0))) { mSpacer.setVisibility(View.INVISIBLE); } else { mSpacer.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 479bd7ada2262..6b9c37fa703c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +64,8 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.telephony.MSimTelephonyManager; +import android.telephony.TelephonyManager; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -97,6 +102,7 @@ import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.MSimSignalClusterView; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.SignalClusterTextView; @@ -111,6 +117,7 @@ import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.IntruderAlertView; import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.MSimNetworkController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NotificationRowLayout; import com.android.systemui.statusbar.policy.OnSizeChangedListener; @@ -173,6 +180,7 @@ public class PhoneStatusBar extends BaseStatusBar { DockBatteryController mDockBatteryController; LocationController mLocationController; NetworkController mNetworkController; + MSimNetworkController mMSimNetworkController; private boolean mHasDockBattery; @@ -293,6 +301,8 @@ public class PhoneStatusBar extends BaseStatusBar { private boolean mBrightnessControl; private float mScreenWidth; private int mMinBrightness; + private int mPeekHeight; + private boolean mJustPeeked; int mLinger; int mInitialTouchX; int mInitialTouchY; @@ -478,8 +488,13 @@ protected PhoneStatusBarView makeStatusBarView() { mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); - mStatusBarWindow = (StatusBarWindowView) View.inflate(context, - R.layout.super_status_bar, null); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + mStatusBarWindow = (StatusBarWindowView) View.inflate(context, + R.layout.msim_super_status_bar, null); + } else { + mStatusBarWindow = (StatusBarWindowView) View.inflate(context, + R.layout.super_status_bar, null); + } mStatusBarWindow.mService = this; mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() { @Override @@ -492,11 +507,21 @@ public boolean onTouch(View v, MotionEvent event) { return mStatusBarWindow.onTouchEvent(event); }}); - mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById( + R.id.msim_status_bar); + } else { + mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); + } mStatusBarView.setBar(this); - PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); + PanelHolder holder; + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.msim_panel_holder); + } else { + holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); + } mStatusBarView.setPanelHolder(holder); mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel); @@ -640,13 +665,11 @@ public boolean onTouch(View v, MotionEvent event) { (DockBatteryController.DockBatteryStateChangeCallback) mCircleDockBattery; mDockBatteryController.addStateChangedCallback(callback); } else { - // Remove dock battery icons if device doesn't hava dock battery support - View v = mStatusBarView.findViewById(R.id.dock_battery); - if (v != null) mStatusBarView.removeView(v); - v = mStatusBarView.findViewById(R.id.dock_battery_text); - if (v != null) mStatusBarView.removeView(v); - v = mStatusBarView.findViewById(R.id.circle_dock_battery); - if (v != null) mStatusBarView.removeView(v); + // Remove dock battery icons if the device doesn't have dock battery support + ViewGroup cluster = (ViewGroup) mStatusBarView.findViewById(R.id.signal_battery_cluster); + cluster.removeView(cluster.findViewById(R.id.dock_battery)); + cluster.removeView(cluster.findViewById(R.id.dock_battery_text)); + cluster.removeView(cluster.findViewById(R.id.circle_dock_battery)); mCircleDockBattery = null; } @@ -657,14 +680,20 @@ public boolean onTouch(View v, MotionEvent event) { mSignalTextView = (SignalClusterTextView) mStatusBarView.findViewById(R.id.signal_cluster_text); mClock = (Clock) mStatusBarView.findViewById(R.id.clock); - mNetworkController.addSignalCluster(mSignalView); mSignalView.setNetworkController(mNetworkController); - final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); - if (isAPhone) { - mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(R.id.emergency_calls_only); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + mMSimNetworkController = new MSimNetworkController(mContext); + MSimSignalClusterView mSimSignalCluster = (MSimSignalClusterView) + mStatusBarView.findViewById(R.id.msim_signal_cluster); + for (int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + mMSimNetworkController.addSignalCluster(mSimSignalCluster, i); + } + mSimSignalCluster.setNetworkController(mMSimNetworkController); + mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById( + R.id.emergency_calls_only); if (mEmergencyCallLabel != null) { - mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); + mMSimNetworkController.addEmergencyLabelView(mEmergencyCallLabel); mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { }}); @@ -675,29 +704,78 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, updateCarrierLabelVisibility(false); }}); } - } - mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); - mShowCarrierInPanel = (mCarrierLabel != null); - if (DEBUG) Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel); - if (mShowCarrierInPanel) { - mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); + mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); + mShowCarrierInPanel = (mCarrierLabel != null); + if (DEBUG) Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel); + if (mShowCarrierInPanel) { + mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); - // for mobile devices, we always show mobile connection info here (SPN/PLMN) - // for other devices, we show whatever network is connected - if (mNetworkController.hasMobileDataFeature()) { - mNetworkController.addMobileLabelView(mCarrierLabel); - } else { - mNetworkController.addCombinedLabelView(mCarrierLabel); + // for mobile devices, we always show mobile connection info here (SPN/PLMN) + // for other devices, we show whatever network is connected + if (mMSimNetworkController.hasMobileDataFeature()) { + mMSimNetworkController.addMobileLabelView(mCarrierLabel); + } else { + mMSimNetworkController.addCombinedLabelView(mCarrierLabel); + } + + // set up the dynamic hide/show of the label + mPile.setOnSizeChangedListener(new OnSizeChangedListener() { + @Override + public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { + updateCarrierLabelVisibility(false); + } + }); + } + } else { + mNetworkController = new NetworkController(mContext); + final SignalClusterView signalCluster = + (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); + + mNetworkController.addSignalCluster(signalCluster); + signalCluster.setNetworkController(mNetworkController); + + final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); + if (isAPhone) { + mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById( + R.id.emergency_calls_only); + if (mEmergencyCallLabel != null) { + mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); + mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { }}); + mEmergencyCallLabel.addOnLayoutChangeListener( + new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + updateCarrierLabelVisibility(false); + }}); + } } - // set up the dynamic hide/show of the label - mPile.setOnSizeChangedListener(new OnSizeChangedListener() { - @Override - public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { - updateCarrierLabelVisibility(false); + mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); + mShowCarrierInPanel = (mCarrierLabel != null); + if (DEBUG) Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + + mShowCarrierInPanel); + if (mShowCarrierInPanel) { + mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); + + // for mobile devices, we always show mobile connection info here (SPN/PLMN) + // for other devices, we show whatever network is connected + if (mNetworkController.hasMobileDataFeature()) { + mNetworkController.addMobileLabelView(mCarrierLabel); + } else { + mNetworkController.addCombinedLabelView(mCarrierLabel); } - }); + + // set up the dynamic hide/show of the label + mPile.setOnSizeChangedListener(new OnSizeChangedListener() { + @Override + public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { + updateCarrierLabelVisibility(false); + } + }); + } } // Quick Settings (where available, some restrictions apply) @@ -1281,10 +1359,18 @@ protected void updateCarrierLabelVisibility(boolean force) { } final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null; - final boolean makeVisible = - !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) - && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight) - && mScrollView.getVisibility() == View.VISIBLE; + final boolean makeVisible; + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + makeVisible = + !(emergencyCallsShownElsewhere && mMSimNetworkController.isEmergencyOnly()) + && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight + - mNotificationHeaderHeight) && mScrollView.getVisibility() == View.VISIBLE; + } else { + makeVisible = + !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) + && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight + - mNotificationHeaderHeight)&& mScrollView.getVisibility() == View.VISIBLE; + } if (force || mCarrierLabelVisible != makeVisible) { mCarrierLabelVisible = makeVisible; @@ -2116,41 +2202,39 @@ private void brightnessControl(MotionEvent event) { final int x = (int) event.getRawX(); final int y = (int) event.getRawY(); if (action == MotionEvent.ACTION_DOWN) { - mLinger = 0; - mInitialTouchX = x; - mInitialTouchY = y; - mVelocityTracker = VelocityTracker.obtain(); - mHandler.removeCallbacks(mLongPressBrightnessChange); - if ((y) < mNotificationHeaderHeight) { + if (y < mNotificationHeaderHeight) { + mLinger = 0; + mInitialTouchX = x; + mInitialTouchY = y; + mJustPeeked = true; + mHandler.removeCallbacks(mLongPressBrightnessChange); mHandler.postDelayed(mLongPressBrightnessChange, BRIGHTNESS_CONTROL_LONG_PRESS_TIMEOUT); } } else if (action == MotionEvent.ACTION_MOVE) { - if ((y) < mNotificationHeaderHeight) { - mVelocityTracker.computeCurrentVelocity(1000); - float yVel = mVelocityTracker.getYVelocity(); - yVel = Math.abs(yVel); - if (yVel < 50.0f) { - if (mLinger > BRIGHTNESS_CONTROL_LINGER_THRESHOLD) { - adjustBrightness(x); - } else { + if (y < mNotificationHeaderHeight && mJustPeeked) { + if (mLinger > BRIGHTNESS_CONTROL_LINGER_THRESHOLD) { + adjustBrightness(x); + } else { + final int xDiff = Math.abs(x - mInitialTouchX); + final int yDiff = Math.abs(y - mInitialTouchY); + final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); + if (xDiff > yDiff) { mLinger++; } - } - int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - if (Math.abs(x - mInitialTouchX) > touchSlop || - Math.abs(y - mInitialTouchY) > touchSlop) { - mHandler.removeCallbacks(mLongPressBrightnessChange); + if (xDiff > touchSlop || yDiff > touchSlop) { + mHandler.removeCallbacks(mLongPressBrightnessChange); + } } } else { + if (y > mPeekHeight) { + mJustPeeked = false; + } mHandler.removeCallbacks(mLongPressBrightnessChange); } } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - mVelocityTracker.recycle(); - mVelocityTracker = null; mHandler.removeCallbacks(mLongPressBrightnessChange); - mLinger = 0; } } @@ -2250,7 +2334,12 @@ private void setStatusBarLowProfile(boolean lightsOut) { if (mLightsOutAnimation == null) { final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area); final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons); - final View signal = mStatusBarView.findViewById(R.id.signal_cluster); + final View signal; + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + signal = mStatusBarView.findViewById(R.id.msim_signal_cluster); + } else { + signal = mStatusBarView.findViewById(R.id.signal_cluster); + } final View signal2 = mStatusBarView.findViewById(R.id.signal_cluster_text); final View battery = mStatusBarView.findViewById(R.id.battery); final View battery2 = mStatusBarView.findViewById(R.id.battery_text); @@ -2540,7 +2629,13 @@ public void run() { mGestureRec.dump(fd, pw, args); } - mNetworkController.dump(fd, pw, args); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + for(int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + mMSimNetworkController.dump(fd, pw, args, i); + } + } else { + mNetworkController.dump(fd, pw, args); + } } @Override @@ -2958,6 +3053,7 @@ protected void loadDimens() { mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height); + mPeekHeight = res.getDimensionPixelSize(R.dimen.peek_height); mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 9b8bd224e7fa4..34e8817e51fb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,11 +38,13 @@ import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import android.util.Slog; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.MSimConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.telephony.cdma.TtyIntent; @@ -77,7 +82,7 @@ public class PhoneStatusBarPolicy { // Assume it's all good unless we hear otherwise. We don't always seem // to get broadcasts that it *is* there. - IccCardConstants.State mSimState = IccCardConstants.State.READY; + IccCardConstants.State[] mSimState; // ringer volume private boolean mVolumeVisible; @@ -150,6 +155,11 @@ public PhoneStatusBarPolicy(Context context) { filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION); mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + mSimState = new IccCardConstants.State[numPhones]; + for (int i=0; i < numPhones; i++) { + mSimState[i] = IccCardConstants.State.READY; + } // storage mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); mStorageManager.registerListener( @@ -206,28 +216,38 @@ private final void updateSyncState(Intent intent) { } private final void updateSimState(Intent intent) { + IccCardConstants.State simState; String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + + // Obtain the subscription info from intent + int sub = intent.getIntExtra(MSimConstants.SUBSCRIPTION_KEY, 0); + Slog.d(TAG, "updateSimState for subscription :" + sub); + if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { - mSimState = IccCardConstants.State.ABSENT; + simState = IccCardConstants.State.ABSENT; + } + else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { + simState = IccCardConstants.State.CARD_IO_ERROR; } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { - mSimState = IccCardConstants.State.READY; + simState = IccCardConstants.State.READY; } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { final String lockedReason = intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { - mSimState = IccCardConstants.State.PIN_REQUIRED; + simState = IccCardConstants.State.PIN_REQUIRED; } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { - mSimState = IccCardConstants.State.PUK_REQUIRED; + simState = IccCardConstants.State.PUK_REQUIRED; } else { - mSimState = IccCardConstants.State.NETWORK_LOCKED; + simState = IccCardConstants.State.PERSO_LOCKED; } } else { - mSimState = IccCardConstants.State.UNKNOWN; + simState = IccCardConstants.State.UNKNOWN; } + mSimState[sub] = simState; } private final void updateVolume() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsController.java index a898942ff3881..9e930c54cafb7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsController.java @@ -33,6 +33,7 @@ import static com.android.internal.util.cm.QSConstants.TILE_NETWORKMODE; import static com.android.internal.util.cm.QSConstants.TILE_NFC; import static com.android.internal.util.cm.QSConstants.TILE_PROFILE; +import static com.android.internal.util.cm.QSConstants.TILE_PERFORMANCE_PROFILE; import static com.android.internal.util.cm.QSConstants.TILE_QUIETHOURS; import static com.android.internal.util.cm.QSConstants.TILE_RINGER; import static com.android.internal.util.cm.QSConstants.TILE_SCREENTIMEOUT; @@ -55,6 +56,7 @@ import android.net.Uri; import android.os.Handler; import android.os.Message; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -79,6 +81,7 @@ import com.android.systemui.quicksettings.MobileNetworkTypeTile; import com.android.systemui.quicksettings.NetworkAdbTile; import com.android.systemui.quicksettings.NfcTile; +import com.android.systemui.quicksettings.PerformanceProfileTile; import com.android.systemui.quicksettings.PreferencesTile; import com.android.systemui.quicksettings.ProfileTile; import com.android.systemui.quicksettings.QuickSettingsTile; @@ -99,6 +102,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; public class QuickSettingsController { private static String TAG = "QuickSettingsController"; @@ -158,6 +162,8 @@ void loadTiles() { boolean bluetoothSupported = QSUtils.deviceSupportsBluetooth(); boolean mobileDataSupported = QSUtils.deviceSupportsMobileData(mContext); boolean lteSupported = QSUtils.deviceSupportsLte(mContext); + boolean gpsSupported = QSUtils.deviceSupportsGps(mContext); + boolean torchSupported = QSUtils.deviceSupportsTorch(mContext); if (!bluetoothSupported) { TILES_DEFAULT.remove(TILE_BLUETOOTH); @@ -173,6 +179,14 @@ void loadTiles() { TILES_DEFAULT.remove(TILE_LTE); } + if (!gpsSupported) { + TILES_DEFAULT.remove(TILE_GPS); + } + + if (!torchSupported) { + TILES_DEFAULT.remove(TILE_TORCH); + } + // Read the stored list of tiles ContentResolver resolver = mContext.getContentResolver(); LayoutInflater inflater = LayoutInflater.from(mContext); @@ -232,6 +246,10 @@ void loadTiles() { if (QSUtils.systemProfilesEnabled(resolver)) { qs = new ProfileTile(mContext, this); } + } else if (tile.equals(TILE_PERFORMANCE_PROFILE)) { + if (QSUtils.deviceSupportsPerformanceProfiles(mContext)) { + qs = new PerformanceProfileTile(mContext, this); + } } else if (tile.equals(TILE_NFC)) { // User cannot add the NFC tile if the device does not support it // No need to check again here diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CircleBattery.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CircleBattery.java index a4348767126e8..50dd38fe117ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CircleBattery.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CircleBattery.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.policy; -import android.view.ViewGroup.LayoutParams; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -207,7 +206,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { initSizeMeasureIconHeight(); } - setMeasuredDimension(mCircleSize + getPaddingLeft(), mCircleSize); + setMeasuredDimension(mCircleSize + getPaddingStart(), mCircleSize); } protected int getBatteryLevel() { @@ -292,6 +291,12 @@ private void updateChargeAnim() { mHandler.postDelayed(mInvalidate, 50); } + @Override + public void onRtlPropertiesChanged(int layoutDirection) { + mRectLeft = null; + invalidate(); + } + /** * initializes all size dependent variables * sets stroke width and text size of all involved paints @@ -317,7 +322,7 @@ private void initSizeBasedStuff() { // calculate Y position for text Rect bounds = new Rect(); mPaintFont.getTextBounds("99", 0, "99".length(), bounds); - mTextLeftX = mCircleSize / 2.0f + getPaddingLeft(); + mTextLeftX = mCircleSize / 2.0f + pLeft; // the +1 at end of formular balances out rounding issues. works out on all resolutions mTextY = mCircleSize / 2.0f + (bounds.bottom - bounds.top) / 2.0f - strokeWidth / 2.0f + 1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MSimNetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MSimNetworkController.java new file mode 100644 index 0000000000000..f950c71418044 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MSimNetworkController.java @@ -0,0 +1,1304 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiManager; +import android.net.wimax.WimaxManagerConstants; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.provider.Settings; +import android.provider.Telephony; +import android.telephony.MSimTelephonyManager; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.util.Slog; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.MSimConstants; +import com.android.internal.telephony.cdma.EriInfo; +import com.android.internal.util.AsyncChannel; + +import com.android.systemui.R; + +public class MSimNetworkController extends NetworkController { + // debug + static final String TAG = "StatusBar.MSimNetworkController"; + static final boolean DEBUG = false; + static final boolean CHATTY = true; // additional diagnostics, but not logspew + + // telephony + private MSimTelephonyManager mPhone; + boolean[] mMSimDataConnected; + IccCardConstants.State[] mMSimState; + int[] mMSimDataActivity; + int[] mMSimDataServiceState; + ServiceState[] mMSimServiceState; + SignalStrength[] mMSimSignalStrength; + private PhoneStateListener[] mMSimPhoneStateListener; + + String[] mMSimNetworkName; + int[] mMSimPhoneSignalIconId; + int[] mMSimLastPhoneSignalIconId; + private int[] mMSimIconId; + int[] mMSimDataDirectionIconId; // data + data direction on phones + int[] mMSimDataSignalIconId; + int[] mMSimDataTypeIconId; + int[] mNoMSimIconId; + int[] mMSimMobileActivityIconId; // overlay arrows for data direction + + String[] mMSimContentDescriptionPhoneSignal; + String[] mMSimContentDescriptionCombinedSignal; + String[] mMSimContentDescriptionDataType; + + int[] mMSimLastDataDirectionIconId; + int[] mMSimLastCombinedSignalIconId; + int[] mMSimLastDataTypeIconId; + int[] mMSimcombinedSignalIconId; + int[] mMSimcombinedActivityIconId; + int[] mMSimLastSimIconId; + private int mDefaultSubscription; + + ArrayList mSimSignalClusters = new ArrayList(); + + public interface MSimSignalCluster { + void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon, + String contentDescription); + void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon, + int typeIcon, String contentDescription, String typeContentDescription, + int noSimIcon, int subscription); + void setIsAirplaneMode(boolean is, int airplaneIcon); + } + + /** + * Construct this controller object and register for updates. + */ + public MSimNetworkController(Context context) { + super(context); + + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + mMSimSignalStrength = new SignalStrength[numPhones]; + mMSimDataServiceState = new int[numPhones]; + mMSimServiceState = new ServiceState[numPhones]; + mMSimState = new IccCardConstants.State[numPhones]; + mMSimIconId = new int[numPhones]; + mMSimPhoneSignalIconId = new int[numPhones]; + mMSimDataTypeIconId = new int[numPhones]; + mNoMSimIconId = new int[numPhones]; + mMSimMobileActivityIconId = new int[numPhones]; + mMSimContentDescriptionPhoneSignal = new String[numPhones]; + mMSimLastPhoneSignalIconId = new int[numPhones]; + mMSimNetworkName = new String[numPhones]; + mMSimLastDataTypeIconId = new int[numPhones]; + mMSimDataConnected = new boolean[numPhones]; + mMSimDataSignalIconId = new int[numPhones]; + mMSimDataDirectionIconId = new int[numPhones]; + mMSimLastDataDirectionIconId = new int[numPhones]; + mMSimLastCombinedSignalIconId = new int[numPhones]; + mMSimcombinedSignalIconId = new int[numPhones]; + mMSimcombinedActivityIconId = new int[numPhones]; + mMSimDataActivity = new int[numPhones]; + mMSimContentDescriptionCombinedSignal = new String[numPhones]; + mMSimContentDescriptionDataType = new String[numPhones]; + mMSimLastSimIconId = new int[numPhones]; + + for (int i=0; i < numPhones; i++) { + mMSimSignalStrength[i] = new SignalStrength(); + mMSimServiceState[i] = new ServiceState(); + mMSimState[i] = IccCardConstants.State.READY; + // phone_signal + mMSimPhoneSignalIconId[i] = R.drawable.stat_sys_signal_null; + mMSimLastPhoneSignalIconId[i] = -1; + mMSimLastDataTypeIconId[i] = -1; + mMSimDataConnected[i] = false; + mMSimLastDataDirectionIconId[i] = -1; + mMSimLastCombinedSignalIconId[i] = -1; + mMSimcombinedSignalIconId[i] = 0; + mMSimcombinedActivityIconId[i] = 0; + mMSimDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; + mMSimLastSimIconId[i] = 0; + mMSimNetworkName[i] = mNetworkNameDefault; + mMSimDataServiceState[i] = ServiceState.STATE_OUT_OF_SERVICE; + } + + mDefaultSubscription = MSimTelephonyManager.getDefault().getDefaultSubscription(); + mDataConnected = mMSimDataConnected[mDefaultSubscription]; + mSimState = mMSimState[mDefaultSubscription]; + mDataActivity = mMSimDataActivity[mDefaultSubscription]; + mDataServiceState = mMSimDataServiceState[mDefaultSubscription]; + mServiceState = mMSimServiceState[mDefaultSubscription]; + mSignalStrength = mMSimSignalStrength[mDefaultSubscription]; + mPhoneStateListener = mMSimPhoneStateListener[mDefaultSubscription]; + + mNetworkName = mMSimNetworkName[mDefaultSubscription]; + mPhoneSignalIconId = mMSimPhoneSignalIconId[mDefaultSubscription]; + mLastPhoneSignalIconId = mMSimLastPhoneSignalIconId[mDefaultSubscription]; + // data + data direction on phones + mDataDirectionIconId = mMSimDataDirectionIconId[mDefaultSubscription]; + mDataSignalIconId = mMSimDataSignalIconId[mDefaultSubscription]; + mDataTypeIconId = mMSimDataTypeIconId[mDefaultSubscription]; + mNoSimIconId = mNoMSimIconId[mDefaultSubscription]; + // overlay arrows for data direction + mMobileActivityIconId = mMSimMobileActivityIconId[mDefaultSubscription]; + + mContentDescriptionPhoneSignal = mMSimContentDescriptionPhoneSignal[mDefaultSubscription]; + mContentDescriptionCombinedSignal = mMSimContentDescriptionCombinedSignal[ + mDefaultSubscription]; + mContentDescriptionDataType = mMSimContentDescriptionDataType[mDefaultSubscription]; + + mLastDataDirectionIconId = mMSimLastDataDirectionIconId[mDefaultSubscription]; + mLastCombinedSignalIconId = mMSimLastCombinedSignalIconId[mDefaultSubscription]; + mLastDataTypeIconId = mMSimLastDataTypeIconId[mDefaultSubscription]; + mLastSimIconId = mMSimLastSimIconId[mDefaultSubscription]; + } + + @Override + protected void createWifiHandler() { + // wifi + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + Handler handler = new MSimWifiHandler(); + mWifiChannel = new AsyncChannel(); + Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); + if (wifiMessenger != null) { + mWifiChannel.connect(mContext, handler, wifiMessenger); + } + } + + @Override + protected void registerPhoneStateListener(Context context) { + // telephony + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + mPhone = (MSimTelephonyManager)context.getSystemService(Context.MSIM_TELEPHONY_SERVICE); + mMSimPhoneStateListener = new PhoneStateListener[numPhones]; + for (int i=0; i < numPhones; i++) { + mMSimPhoneStateListener[i] = getPhoneStateListener(i); + mPhone.listen(mMSimPhoneStateListener[i], + PhoneStateListener.LISTEN_SERVICE_STATE + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS + | PhoneStateListener.LISTEN_CALL_STATE + | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_DATA_ACTIVITY); + } + } + + public void addSignalCluster(MSimSignalCluster cluster, int subscription) { + mSimSignalClusters.add(cluster); + refreshSignalCluster(cluster, subscription); + } + + public void refreshSignalCluster(MSimSignalCluster cluster, int subscription) { + cluster.setWifiIndicators( + // only show wifi in the cluster if connected or if wifi-only + mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature), + mWifiIconId, + mWifiActivityIconId, + mContentDescriptionWifi); + cluster.setMobileDataIndicators( + mHasMobileDataFeature, + mMSimPhoneSignalIconId[subscription], + mMSimMobileActivityIconId[subscription], + mMSimDataTypeIconId[subscription], + mMSimContentDescriptionPhoneSignal[subscription], + mMSimContentDescriptionDataType[subscription], + mNoMSimIconId[subscription], subscription); + if (mIsWimaxEnabled && mWimaxConnected) { + // wimax is special + cluster.setMobileDataIndicators( + true, + mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId, + mMSimMobileActivityIconId[subscription], + mMSimDataTypeIconId[subscription], + mContentDescriptionWimax, + mMSimContentDescriptionDataType[subscription], + mNoMSimIconId[subscription], subscription); + } else { + // normal mobile data + cluster.setMobileDataIndicators( + mHasMobileDataFeature, + mShowPhoneRSSIForData ? mMSimPhoneSignalIconId[subscription] + : mMSimDataSignalIconId[subscription], + mMSimMobileActivityIconId[subscription], + mMSimDataTypeIconId[subscription], + mMSimContentDescriptionPhoneSignal[subscription], + mMSimContentDescriptionDataType[subscription], + mNoMSimIconId[subscription], subscription); + } + cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId); + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action.equals(WifiManager.RSSI_CHANGED_ACTION) + || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) + || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + updateWifiState(intent); + refreshViews(mDefaultSubscription); + } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { + updateSimState(intent); + for (int sub = 0; sub < MSimTelephonyManager.getDefault().getPhoneCount(); sub++) { + updateDataIcon(sub); + refreshViews(sub); + } + } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { + final int subscription = intent.getIntExtra(MSimConstants.SUBSCRIPTION_KEY, 0); + Slog.d(TAG, "Received SPN update on sub :" + subscription); + updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), + intent.getStringExtra(TelephonyIntents.EXTRA_SPN), + intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), + intent.getStringExtra(TelephonyIntents.EXTRA_PLMN), subscription); + refreshViews(subscription); + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || + action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { + updateConnectivity(intent); + refreshViews(mDefaultSubscription); + } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + refreshViews(mDefaultSubscription); + } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { + updateAirplaneMode(); + for (int i = 0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + updateSimIcon(i); + } + refreshViews(mDefaultSubscription); + } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) || + action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) || + action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { + updateWimaxState(intent); + refreshViews(mDefaultSubscription); + } + } + + // ===== Telephony ============================================================== + + private PhoneStateListener getPhoneStateListener(int subscription) { + PhoneStateListener mMSimPhoneStateListener = new PhoneStateListener(subscription) { + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + if (DEBUG) { + Slog.d(TAG, "onSignalStrengthsChanged received on subscription :" + + mSubscription + "signalStrength=" + signalStrength + + ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); + } + mMSimSignalStrength[mSubscription] = signalStrength; + updateTelephonySignalStrength(mSubscription); + refreshViews(mSubscription); + } + + @Override + public void onServiceStateChanged(ServiceState state) { + if (DEBUG) { + Slog.d(TAG, "onServiceStateChanged received on subscription :" + + mSubscription + "state=" + state.getState()); + } + mMSimServiceState[mSubscription] = state; + updateTelephonySignalStrength(mSubscription); + updateDataNetType(mSubscription); + updateDataIcon(mSubscription); + refreshViews(mSubscription); + } + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + if (DEBUG) { + Slog.d(TAG, "onCallStateChanged received on subscription :" + + mSubscription + "state=" + state); + } + // In cdma, if a voice call is made, RSSI should switch to 1x. + if (isCdma(mSubscription)) { + updateTelephonySignalStrength(mSubscription); + refreshViews(mSubscription); + } + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + if (DEBUG) { + Slog.d(TAG, "onDataConnectionStateChanged received on subscription :" + + mSubscription + "state=" + state + " type=" + networkType); + } + + // DSDS case: Data is active only on DDS. Ignore the Data Connection + // State changed notifications of the other NON-DDS. + if (mSubscription == + MSimTelephonyManager.getDefault().getPreferredDataSubscription()) { + mDataState = state; + mDataNetType = networkType; + } + updateDataNetType(mSubscription); + updateDataIcon(mSubscription); + refreshViews(mSubscription); + } + + @Override + public void onDataActivity(int direction) { + if (DEBUG) { + Slog.d(TAG, "onDataActivity received on subscription :" + + mSubscription + "direction=" + direction); + } + mMSimDataActivity[mSubscription] = direction; + updateDataIcon(mSubscription); + refreshViews(mSubscription); + } + }; + return mMSimPhoneStateListener; + } + + // ===== Wifi =================================================================== + + class MSimWifiHandler extends WifiHandler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case WifiManager.DATA_ACTIVITY_NOTIFICATION: + if (msg.arg1 != mWifiActivity) { + mWifiActivity = msg.arg1; + refreshViews(MSimTelephonyManager.getDefault(). + getPreferredDataSubscription()); + } + break; + default: + super.handleMessage(msg); + break; + } + } + } + + @Override + protected void updateSimState(Intent intent) { + IccCardConstants.State simState; + String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + // Obtain the subscription info from intent. + int sub = intent.getIntExtra(MSimConstants.SUBSCRIPTION_KEY, 0); + Slog.d(TAG, "updateSimState for subscription :" + sub); + if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { + simState = IccCardConstants.State.ABSENT; + } + else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { + simState = IccCardConstants.State.READY; + } + else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { + final String lockedReason = intent.getStringExtra(IccCardConstants. + INTENT_KEY_LOCKED_REASON); + if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { + simState = IccCardConstants.State.PIN_REQUIRED; + } + else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { + simState = IccCardConstants.State.PUK_REQUIRED; + } + else { + simState = IccCardConstants.State.PERSO_LOCKED; + } + } else { + simState = IccCardConstants.State.UNKNOWN; + } + mMSimState[sub] = simState; + Slog.d(TAG, "updateSimState simState =" + mMSimState[sub]); + updateSimIcon(sub); + updateDataIcon(sub); + } + + private boolean isCdma(int subscription) { + return (mMSimSignalStrength[subscription] != null) && + !mMSimSignalStrength[subscription].isGsm(); + } + + private boolean hasService(int subscription) { + ServiceState ss = mMSimServiceState[subscription]; + if (ss != null) { + switch (ss.getState()) { + case ServiceState.STATE_OUT_OF_SERVICE: + case ServiceState.STATE_POWER_OFF: + return false; + default: + return true; + } + } else { + return false; + } + } + + private final void updateTelephonySignalStrength(int subscription) { + Slog.d(TAG, "updateTelephonySignalStrength: subscription =" + subscription); + if (!hasService(subscription) && + (mMSimDataServiceState[subscription] != ServiceState.STATE_IN_SERVICE)) { + if (DEBUG) Slog.d(TAG, " No service"); + mMSimPhoneSignalIconId[subscription] = R.drawable.stat_sys_signal_null; + mMSimDataSignalIconId[subscription] = R.drawable.stat_sys_signal_null; + } else { + if (mMSimSignalStrength[subscription] == null || (mMSimServiceState == null)) { + if (DEBUG) { + Slog.d(TAG, " Null object, mMSimSignalStrength= " + + mMSimSignalStrength[subscription] + + " mMSimServiceState " + mMSimServiceState[subscription]); + } + mMSimPhoneSignalIconId[subscription] = R.drawable.stat_sys_signal_null; + mMSimDataSignalIconId[subscription] = R.drawable.stat_sys_signal_null; + mMSimContentDescriptionPhoneSignal[subscription] = mContext.getString( + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); + } else { + int iconLevel; + int[] iconList; + if (isCdma(subscription) && mAlwaysShowCdmaRssi) { + mLastSignalLevel = iconLevel = mMSimSignalStrength[subscription].getCdmaLevel(); + if(DEBUG) Slog.d(TAG, "mAlwaysShowCdmaRssi= " + mAlwaysShowCdmaRssi + + " set to cdmaLevel= " + + mMSimSignalStrength[subscription].getCdmaLevel() + + " instead of level= " + mMSimSignalStrength[subscription].getLevel()); + } else { + mLastSignalLevel = iconLevel = mMSimSignalStrength[subscription].getLevel(); + } + + // Though mPhone is a Manager, this call is not an IPC + if ((isCdma(subscription) && isCdmaEri(subscription)) || + mPhone.isNetworkRoaming(subscription)) { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; + } else { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; + } + + Slog.d(TAG, "updateTelephonySignalStrength iconList = " + iconList + "iconLevel = " + + iconLevel + " mInetCondition = " + mInetCondition); + mMSimPhoneSignalIconId[subscription] = iconList[iconLevel]; + mMSimContentDescriptionPhoneSignal[subscription] = mContext.getString( + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]); + + mMSimDataSignalIconId[subscription] = TelephonyIcons + .DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; + } + } + } + + private final void updateDataNetType(int subscription) { + // DSDS case: Data is active only on DDS. Clear the icon for NON-DDS + int dataSub = MSimTelephonyManager.getDefault().getPreferredDataSubscription(); + if (subscription != dataSub) { + Slog.d(TAG,"updateDataNetType: SUB" + subscription + + " is not DDS(=SUB" + dataSub + ")!"); + mMSimDataTypeIconId[subscription] = 0; + } else { + if (mIsWimaxEnabled && mWimaxConnected) { + // wimax is a special 4g network not handled by telephony + mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_4g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_4g); + } else { + Slog.d(TAG,"updateDataNetType sub = " + subscription + + " mDataNetType = " + mDataNetType); + switch (mDataNetType) { + case TelephonyManager.NETWORK_TYPE_UNKNOWN: + if (DEBUG) { + Slog.e(TAG, "updateDataNetType NETWORK_TYPE_UNKNOWN"); + } + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; + mMSimDataTypeIconId[subscription] = 0; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_gprs); + break; + } else { + // fall through + } + case TelephonyManager.NETWORK_TYPE_EDGE: + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_E[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_e; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_edge); + break; + } else { + // fall through + } + case TelephonyManager.NETWORK_TYPE_UMTS: + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_3g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_3g); + break; + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_HSPAP: + if (mHspaDataDistinguishable) { + mDataIconList = TelephonyIcons.DATA_H[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable. + stat_sys_data_connected_h; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_3_5g); + } else { + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable. + stat_sys_data_connected_3g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_3g); + } + break; + case TelephonyManager.NETWORK_TYPE_CDMA: + // display 1xRTT for IS95A/B + mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_1x; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_cdma); + break; + case TelephonyManager.NETWORK_TYPE_1xRTT: + mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_1x; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_cdma); + break; + case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_3g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_3g); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_4g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_4g); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_gprs); + } else { + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mMSimDataTypeIconId[subscription] = + R.drawable.stat_sys_data_connected_3g; + mMSimContentDescriptionDataType[subscription] = mContext.getString( + R.string.accessibility_data_connection_3g); + } + break; + default: + if (DEBUG) { + Slog.e(TAG, "updateDataNetType unknown radio:" + mDataNetType); + } + mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + mMSimDataTypeIconId[subscription] = 0; + break; + } + } + } + + if (isCdma(subscription)) { + if (isCdmaEri(subscription)) { + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_roam; + } + } else if (mPhone.isNetworkRoaming(subscription)) { + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_roam; + } + } + + boolean isCdmaEri(int subscription) { + if ((mMSimServiceState[subscription] != null) + && (hasService(subscription) || (mMSimDataServiceState[subscription] + == ServiceState.STATE_IN_SERVICE))) { + final int iconIndex = mMSimServiceState[subscription].getCdmaEriIconIndex(); + if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { + final int iconMode = mMSimServiceState[subscription].getCdmaEriIconMode(); + if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL + || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { + return true; + } + } + } + return false; + } + + private final void updateSimIcon(int cardIndex) { + Slog.d(TAG,"In updateSimIcon card =" + cardIndex + ", simState= " + mMSimState[cardIndex]); + if (mMSimState[cardIndex] == IccCardConstants.State.ABSENT) { + mNoMSimIconId[cardIndex] = R.drawable.stat_sys_no_sim; + } else { + mNoMSimIconId[cardIndex] = 0; + } + refreshViews(cardIndex); + } + + private final void updateDataIcon(int subscription) { + Slog.d(TAG,"updateDataIcon subscription =" + subscription); + int iconId = 0; + boolean visible = true; + + int dataSub = MSimTelephonyManager.getDefault().getPreferredDataSubscription(); + Slog.d(TAG,"updateDataIcon dataSub =" + dataSub); + // DSDS case: Data is active only on DDS. Clear the icon for NON-DDS + if (subscription != dataSub) { + mMSimDataConnected[subscription] = false; + Slog.d(TAG,"updateDataIconi: SUB" + subscription + + " is not DDS. Clear the mMSimDataConnected Flag and return"); + return; + } + + Slog.d(TAG,"updateDataIcon when SimState =" + mMSimState[subscription]); + if (mDataNetType == TelephonyManager.NETWORK_TYPE_UNKNOWN) { + // If data network type is unknown do not display data icon + visible = false; + } else if (!isCdma(subscription)) { + Slog.d(TAG,"updateDataIcon when gsm mMSimState =" + mMSimState[subscription]); + // GSM case, we have to check also the sim state + if (mMSimState[subscription] == IccCardConstants.State.READY || + mMSimState[subscription] == IccCardConstants.State.UNKNOWN) { + if (mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mMSimDataActivity[subscription]) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + default: + iconId = mDataIconList[0]; + break; + } + mMSimDataDirectionIconId[subscription] = iconId; + } else { + iconId = 0; + visible = false; + } + } else { + Slog.d(TAG,"updateDataIcon when no sim"); + iconId = R.drawable.stat_sys_no_sim; + visible = false; // no SIM? no data + } + } else { + // CDMA case, mMSimDataActivity can be also DATA_ACTIVITY_DORMANT + if (mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mMSimDataActivity[subscription]) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + default: + iconId = mDataIconList[0]; + break; + } + } else { + iconId = 0; + visible = false; + } + } + + // yuck - this should NOT be done by the status bar + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.notePhoneDataConnectionState(mPhone. + getNetworkType(subscription), visible); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + + mMSimDataDirectionIconId[subscription] = iconId; + mMSimDataConnected[subscription] = visible; + Slog.d(TAG,"updateDataIcon when mMSimDataConnected =" + mMSimDataConnected[subscription]); + } + + void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn, + int subscription) { + if (false) { + Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + + " showPlmn=" + showPlmn + " plmn=" + plmn); + } + StringBuilder str = new StringBuilder(); + boolean something = false; + if (showPlmn && plmn != null) { + str.append(plmn); + something = true; + } + if (showSpn && spn != null) { + if (something) { + str.append(mNetworkNameSeparator); + } + str.append(spn); + something = true; + } + if (something) { + mMSimNetworkName[subscription] = str.toString(); + } else { + mMSimNetworkName[subscription] = mNetworkNameDefault; + } + } + + // ===== Full or limited Internet connectivity ================================== + @Override + protected void updateConnectivity(Intent intent) { + if (CHATTY) { + Slog.d(TAG, "updateConnectivity: intent=" + intent); + } + + final ConnectivityManager connManager = (ConnectivityManager) mContext + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo info = connManager.getActiveNetworkInfo(); + + // Are we connected at all, by any interface? + mConnected = info != null && info.isConnected(); + if (mConnected) { + mConnectedNetworkType = info.getType(); + mConnectedNetworkTypeName = info.getTypeName(); + } else { + mConnectedNetworkType = ConnectivityManager.TYPE_NONE; + mConnectedNetworkTypeName = null; + } + + int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); + + if (CHATTY) { + Slog.d(TAG, "updateConnectivity: networkInfo=" + info); + Slog.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); + } + + mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); + if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) { + mBluetoothTethered = info.isConnected(); + } else { + mBluetoothTethered = false; + } + + // We want to update all the icons, all at once, for any condition change + updateWimaxIcons(); + for (int sub = 0; sub < MSimTelephonyManager.getDefault().getPhoneCount(); sub++) { + updateDataNetType(sub); + updateDataIcon(sub); + updateTelephonySignalStrength(sub); + } + updateWifiIcons(); + } + + // ===== Update the views ======================================================= + + protected void refreshViews(int subscription) { + Context context = mContext; + + String combinedLabel = ""; + String mobileLabel = ""; + String wifiLabel = ""; + int N; + Slog.d(TAG,"refreshViews subscription =" + subscription + "mMSimDataConnected =" + + mMSimDataConnected[subscription]); + Slog.d(TAG,"refreshViews mMSimDataActivity =" + mMSimDataActivity[subscription]); + if (!mHasMobileDataFeature) { + mMSimDataSignalIconId[subscription] = mMSimPhoneSignalIconId[subscription] = 0; + mobileLabel = ""; + } else { + // We want to show the carrier name if in service and either: + // - We are connected to mobile data, or + // - We are not connected to mobile data, as long as the *reason* packets are not + // being routed over that link is that we have better connectivity via wifi. + // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) + // is connected, we show nothing. + // Otherwise (nothing connected) we show "No internet connection". + + if (mMSimDataConnected[subscription]) { + mobileLabel = mMSimNetworkName[subscription]; + } else if (mConnected) { + if (hasService(subscription)) { + mobileLabel = mMSimNetworkName[subscription]; + } else { + mobileLabel = ""; + } + } else { + mobileLabel + = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + } + + // Now for things that should only be shown when actually using mobile data. + if (mMSimDataConnected[subscription]) { + mMSimcombinedSignalIconId[subscription] = mMSimDataSignalIconId[subscription]; + switch (mMSimDataActivity[subscription]) { + case TelephonyManager.DATA_ACTIVITY_IN: + mMSimMobileActivityIconId[subscription] = R.drawable.stat_sys_signal_in; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + mMSimMobileActivityIconId[subscription] = R.drawable.stat_sys_signal_out; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + mMSimMobileActivityIconId[subscription] = R.drawable.stat_sys_signal_inout; + break; + default: + mMSimMobileActivityIconId[subscription] = 0; + break; + } + + combinedLabel = mobileLabel; + mMSimcombinedActivityIconId[subscription] = mMSimMobileActivityIconId[subscription]; + // set by updateDataIcon() + mMSimcombinedSignalIconId[subscription] = mMSimDataSignalIconId[subscription]; + mMSimContentDescriptionCombinedSignal[subscription] = + mMSimContentDescriptionDataType[subscription]; + } + } + + if (mWifiConnected) { + if (mWifiSsid == null) { + wifiLabel = context.getString( + R.string.status_bar_settings_signal_meter_wifi_nossid); + mWifiActivityIconId = 0; // no wifis, no bits + } else { + wifiLabel = mWifiSsid; + if (DEBUG) { + wifiLabel += "xxxxXXXXxxxxXXXX"; + } + switch (mWifiActivity) { + case WifiManager.DATA_ACTIVITY_IN: + mWifiActivityIconId = R.drawable.stat_sys_wifi_in; + break; + case WifiManager.DATA_ACTIVITY_OUT: + mWifiActivityIconId = R.drawable.stat_sys_wifi_out; + break; + case WifiManager.DATA_ACTIVITY_INOUT: + mWifiActivityIconId = R.drawable.stat_sys_wifi_inout; + break; + case WifiManager.DATA_ACTIVITY_NONE: + mWifiActivityIconId = 0; + break; + } + } + + mMSimcombinedActivityIconId[subscription] = mWifiActivityIconId; + combinedLabel = wifiLabel; + mMSimcombinedSignalIconId[subscription] = mWifiIconId; // set by updateWifiIcons() + mMSimContentDescriptionCombinedSignal[subscription] = mContentDescriptionWifi; + } else { + if (mHasMobileDataFeature) { + wifiLabel = ""; + } else { + wifiLabel = context.getString( + R.string.status_bar_settings_signal_meter_disconnected); + } + } + + if (mBluetoothTethered) { + combinedLabel = mContext.getString(R.string.bluetooth_tethered); + mMSimcombinedSignalIconId[subscription] = mBluetoothTetherIconId; + mMSimContentDescriptionCombinedSignal[subscription] = mContext.getString( + R.string.accessibility_bluetooth_tether); + } + + final boolean ethernetConnected = (mConnectedNetworkType == + ConnectivityManager.TYPE_ETHERNET); + if (ethernetConnected) { + // TODO: icons and strings for Ethernet connectivity + combinedLabel = mConnectedNetworkTypeName; + } + + if (mAirplaneMode && + (mMSimServiceState[subscription] == null || (!hasService(subscription) + && !mMSimServiceState[subscription].isEmergencyOnly()))) { + // Only display the flight-mode icon if not in "emergency calls only" mode. + + // look again; your radios are now airplanes + mMSimContentDescriptionPhoneSignal[subscription] = mContext.getString( + R.string.accessibility_airplane_mode); + mAirplaneIconId = R.drawable.stat_sys_signal_flightmode; + mMSimPhoneSignalIconId[subscription] = mMSimDataSignalIconId[subscription] + = mMSimDataTypeIconId[subscription] = 0; + mNoMSimIconId[subscription] = 0; + + // combined values from connected wifi take precedence over airplane mode + if (mWifiConnected) { + // Suppress "No internet connection." from mobile if wifi connected. + mobileLabel = ""; + } else { + if (mHasMobileDataFeature) { + // let the mobile icon show "No internet connection." + wifiLabel = ""; + } else { + wifiLabel = context.getString( + R.string.status_bar_settings_signal_meter_disconnected); + combinedLabel = wifiLabel; + } + mMSimContentDescriptionCombinedSignal[subscription] = + mContentDescriptionPhoneSignal; + mMSimcombinedSignalIconId[subscription] = mMSimDataSignalIconId[subscription]; + } + mMSimDataTypeIconId[subscription] = 0; + mNoMSimIconId[subscription] = 0; + + mMSimcombinedSignalIconId[subscription] = mMSimDataSignalIconId[subscription]; + } + else if (!mMSimDataConnected[subscription] && !mWifiConnected && !mBluetoothTethered && + !mWimaxConnected && !ethernetConnected) { + // pretty much totally disconnected + + combinedLabel = context.getString( + R.string.status_bar_settings_signal_meter_disconnected); + // On devices without mobile radios, we want to show the wifi icon + mMSimcombinedSignalIconId[subscription] = + mHasMobileDataFeature ? mMSimDataSignalIconId[subscription] : mWifiIconId; + mMSimContentDescriptionCombinedSignal[subscription] = mHasMobileDataFeature + ? mMSimContentDescriptionDataType[subscription] : mContentDescriptionWifi; + + mMSimDataTypeIconId[subscription] = 0; + if (isCdma(subscription)) { + if (isCdmaEri(subscription)) { + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_roam; + } + } else if (mPhone.isNetworkRoaming(subscription)) { + mMSimDataTypeIconId[subscription] = R.drawable.stat_sys_data_connected_roam; + } + } + + if (!mAirplaneMode && mMSimState[subscription] == IccCardConstants.State.ABSENT) { + mMSimPhoneSignalIconId[subscription] = mMSimDataSignalIconId[subscription] + = mMSimDataTypeIconId[subscription] = 0; + } + + if (DEBUG) { + Slog.d(TAG, "refreshViews connected={" + + (mWifiConnected?" wifi":"") + + (mMSimDataConnected[subscription]?" data":"") + + " } level=" + + ((mMSimSignalStrength[subscription] == null)?"??":Integer.toString + (mMSimSignalStrength[subscription].getLevel())) + + " mMSimcombinedSignalIconId=0x" + + Integer.toHexString(mMSimcombinedSignalIconId[subscription]) + + "/" + getResourceName(mMSimcombinedSignalIconId[subscription]) + + " mMSimcombinedActivityIconId=0x" + Integer.toHexString + (mMSimcombinedActivityIconId[subscription]) + + " mAirplaneMode=" + mAirplaneMode + + " mMSimDataActivity=" + mMSimDataActivity[subscription] + + " mMSimPhoneSignalIconId=0x" + Integer.toHexString + (mMSimPhoneSignalIconId[subscription]) + + " mMSimDataDirectionIconId=0x" + Integer.toHexString + (mMSimDataDirectionIconId[subscription]) + + " mMSimDataSignalIconId=0x" + Integer.toHexString + (mMSimDataSignalIconId[subscription]) + + " mMSimDataTypeIconId=0x" + Integer.toHexString + (mMSimDataTypeIconId[subscription]) + + " mNoMSimIconId=0x" + Integer.toHexString(mNoMSimIconId[subscription]) + + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) + + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); + } + + if (mMSimLastPhoneSignalIconId[subscription] != mMSimPhoneSignalIconId[subscription] + || mLastDataDirectionOverlayIconId != mMSimcombinedActivityIconId[subscription] + || mLastWifiIconId != mWifiIconId + || mLastWimaxIconId != mWimaxIconId + || mMSimLastDataTypeIconId[subscription] != mMSimDataTypeIconId[subscription] + || mLastAirplaneMode != mAirplaneMode + || mMSimLastSimIconId[subscription] != mNoMSimIconId[subscription]) + { + // NB: the mLast*s will be updated later + for (MSimSignalCluster cluster : mSimSignalClusters) { + refreshSignalCluster(cluster, subscription); + } + } + + if (mLastAirplaneMode != mAirplaneMode) { + mLastAirplaneMode = mAirplaneMode; + } + + // the phone icon on phones + if (mMSimLastPhoneSignalIconId[subscription] != mMSimPhoneSignalIconId[subscription]) { + mMSimLastPhoneSignalIconId[subscription] = mMSimPhoneSignalIconId[subscription]; + N = mPhoneSignalIconViews.size(); + for (int i=0; i mPhoneSignalIconViews = new ArrayList(); ArrayList mDataDirectionIconViews = new ArrayList(); ArrayList mDataDirectionOverlayIconViews = new ArrayList(); @@ -168,7 +175,7 @@ public class NetworkController extends BroadcastReceiver { int mLastDataTypeIconId = -1; String mLastCombinedLabel = ""; - private boolean mHasMobileDataFeature; + protected boolean mHasMobileDataFeature; boolean mDataAndWifiStacked = false; @@ -179,7 +186,8 @@ public interface SignalCluster { void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon, String contentDescription); void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon, - int typeIcon, String contentDescription, String typeContentDescription); + int typeIcon, String contentDescription, String typeContentDescription, + int noSimIcon); void setIsAirplaneMode(boolean is, int airplaneIcon); void setEtherIndicators(boolean visible, int etherIcon, String contentDescription); } @@ -214,13 +222,7 @@ public NetworkController(Context context) { updateWimaxIcons(); // telephony - mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - mPhone.listen(mPhoneStateListener, - PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS - | PhoneStateListener.LISTEN_CALL_STATE - | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_DATA_ACTIVITY); + registerPhoneStateListener(context); mHspaDataDistinguishable = mContext.getResources().getBoolean( R.bool.config_hspa_data_distinguishable); mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator); @@ -228,14 +230,7 @@ public NetworkController(Context context) { com.android.internal.R.string.lockscreen_carrier_default); mNetworkName = mNetworkNameDefault; - // wifi - mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - Handler handler = new WifiHandler(); - mWifiChannel = new AsyncChannel(); - Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); - if (wifiMessenger != null) { - mWifiChannel.connect(mContext, handler, wifiMessenger); - } + createWifiHandler(); // broadcasts IntentFilter filter = new IntentFilter(); @@ -278,6 +273,28 @@ public boolean isEmergencyOnly() { return (mServiceState != null && mServiceState.isEmergencyOnly()); } + protected void createWifiHandler() { + // wifi + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + Handler handler = new WifiHandler(); + mWifiChannel = new AsyncChannel(); + Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); + if (wifiMessenger != null) { + mWifiChannel.connect(mContext, handler, wifiMessenger); + } + } + + protected void registerPhoneStateListener(Context context) { + // telephony + mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + mPhone.listen(mPhoneStateListener, + PhoneStateListener.LISTEN_SERVICE_STATE + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS + | PhoneStateListener.LISTEN_CALL_STATE + | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_DATA_ACTIVITY); + } + public void addPhoneSignalIconView(ImageView v) { mPhoneSignalIconViews.add(v); } @@ -357,7 +374,8 @@ public void refreshSignalCluster(SignalCluster cluster) { mMobileActivityIconId, mDataTypeIconId, mContentDescriptionWimax, - mContentDescriptionDataType); + mContentDescriptionDataType, + mNoSimIconId); } else { // normal mobile data cluster.setMobileDataIndicators( @@ -366,7 +384,8 @@ public void refreshSignalCluster(SignalCluster cluster) { mMobileActivityIconId, mDataTypeIconId, mContentDescriptionPhoneSignal, - mContentDescriptionDataType); + mContentDescriptionDataType, + mNoSimIconId); } cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId); } @@ -430,6 +449,7 @@ public void onReceive(Context context, Intent intent) { } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { refreshLocale(); updateAirplaneMode(); + updateSimIcon(); refreshViews(); } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) || action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) || @@ -457,9 +477,19 @@ public void onSignalStrengthsChanged(SignalStrength signalStrength) { @Override public void onServiceStateChanged(ServiceState state) { if (DEBUG) { - Slog.d(TAG, "onServiceStateChanged state=" + state.getState()); + Slog.d(TAG, "onServiceStateChanged state=" + state); } mServiceState = state; + if (SystemProperties.getBoolean("ro.config.combined_signal", true)) { + /* + * if combined_signal is set to true only then consider data + * service state for signal display + */ + mDataServiceState = mServiceState.getDataRegState(); + if (DEBUG) { + Slog.d(TAG, "Combining data service state" + mDataServiceState + "for signal"); + } + } updateTelephonySignalStrength(); updateDataNetType(); updateDataIcon(); @@ -502,11 +532,14 @@ public void onDataActivity(int direction) { } }; - private final void updateSimState(Intent intent) { + protected void updateSimState(Intent intent) { String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { mSimState = IccCardConstants.State.ABSENT; } + else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { + mSimState = IccCardConstants.State.CARD_IO_ERROR; + } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { mSimState = IccCardConstants.State.READY; } @@ -520,11 +553,12 @@ else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { mSimState = IccCardConstants.State.PUK_REQUIRED; } else { - mSimState = IccCardConstants.State.NETWORK_LOCKED; + mSimState = IccCardConstants.State.PERSO_LOCKED; } } else { mSimState = IccCardConstants.State.UNKNOWN; } + updateSimIcon(); } private boolean isCdma() { @@ -545,7 +579,7 @@ private boolean hasService() { } } - private void updateAirplaneMode() { + protected void updateAirplaneMode() { mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1); } @@ -555,14 +589,18 @@ private void refreshLocale() { } private final void updateTelephonySignalStrength() { - if (!hasService()) { - if (CHATTY) Slog.d(TAG, "updateTelephonySignalStrength: !hasService()"); + if (!hasService() && + (mDataServiceState != ServiceState.STATE_IN_SERVICE)) { + if (DEBUG) Slog.d(TAG, " No service"); mPhoneSignalIconId = R.drawable.stat_sys_signal_null; mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; mDataSignalIconId = R.drawable.stat_sys_signal_null; } else { - if (mSignalStrength == null) { - if (CHATTY) Slog.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null"); + if ((mSignalStrength == null) || (mServiceState == null)) { + if (DEBUG) { + Slog.d(TAG, " Null object, mSignalStrength= " + mSignalStrength + + " mServiceState " + mServiceState); + } mPhoneSignalIconId = R.drawable.stat_sys_signal_null; mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; mDataSignalIconId = R.drawable.stat_sys_signal_null; @@ -580,20 +618,13 @@ private final void updateTelephonySignalStrength() { mLastSignalLevel = iconLevel = mSignalStrength.getLevel(); } - if (isCdma()) { - if (isCdmaEri()) { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; - } else { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; - } + // Though mPhone is a Manager, this call is not an IPC + if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; } else { - // Though mPhone is a Manager, this call is not an IPC - if (mPhone.isNetworkRoaming()) { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; - } else { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; - } + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; } + mPhoneSignalIconId = iconList[iconLevel]; mQSPhoneSignalIconId = TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel]; @@ -615,6 +646,9 @@ private final void updateDataNetType() { } else { switch (mDataNetType) { case TelephonyManager.NETWORK_TYPE_UNKNOWN: + if (DEBUG) { + Slog.e(TAG, "updateDataNetType NETWORK_TYPE_UNKNOWN"); + } if (!mShowAtLeastThreeGees) { mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; mDataTypeIconId = 0; @@ -637,6 +671,7 @@ private final void updateDataNetType() { // fall through } case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_3g; mQSDataTypeIconId = R.drawable.ic_qs_signal_3g; @@ -721,7 +756,7 @@ private final void updateDataNetType() { R.string.accessibility_data_connection_lte); } break; - default: + case TelephonyManager.NETWORK_TYPE_GPRS: if (!mShowAtLeastThreeGees) { mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_g; @@ -736,6 +771,13 @@ private final void updateDataNetType() { R.string.accessibility_data_connection_3g); } break; + default: + if (DEBUG) { + Slog.e(TAG, "updateDataNetType unknown radio:" + mDataNetType); + } + mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + mDataTypeIconId = 0; + break; } } @@ -751,7 +793,8 @@ private final void updateDataNetType() { } boolean isCdmaEri() { - if (mServiceState != null) { + if ((mServiceState != null) + && (hasService() || (mDataServiceState == ServiceState.STATE_IN_SERVICE))) { final int iconIndex = mServiceState.getCdmaEriIconIndex(); if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { final int iconMode = mServiceState.getCdmaEriIconMode(); @@ -764,15 +807,27 @@ boolean isCdmaEri() { return false; } + private final void updateSimIcon() { + Slog.d(TAG,"In updateSimIcon simState= " + mSimState); + if (mSimState == IccCardConstants.State.ABSENT) { + mNoSimIconId = R.drawable.stat_sys_no_sim; + } else { + mNoSimIconId = 0; + } + refreshViews(); + } + private final void updateDataIcon() { - int iconId; + int iconId = 0; boolean visible = true; - - if (!isCdma()) { + if (mDataNetType == TelephonyManager.NETWORK_TYPE_UNKNOWN) { + // If data network type is unknown do not display data icon + visible = false; + } else if (!isCdma()) { // GSM case, we have to check also the sim state if (mSimState == IccCardConstants.State.READY || mSimState == IccCardConstants.State.UNKNOWN) { - if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + if (mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: iconId = mDataIconList[1]; @@ -798,7 +853,7 @@ private final void updateDataIcon() { } } else { // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT - if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + if (mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: iconId = mDataIconList[1]; @@ -885,7 +940,7 @@ public void handleMessage(Message msg) { } } - private void updateWifiState(Intent intent) { + protected void updateWifiState(Intent intent) { final String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, @@ -920,7 +975,7 @@ private void updateWifiState(Intent intent) { updateWifiIcons(); } - private void updateWifiIcons() { + protected void updateWifiIcons() { if (mWifiConnected) { mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; @@ -955,7 +1010,7 @@ private String huntForSsid(WifiInfo info) { // ===== Wimax =================================================================== - private final void updateWimaxState(Intent intent) { + protected final void updateWimaxState(Intent intent) { final String action = intent.getAction(); boolean wasConnected = mWimaxConnected; if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) { @@ -979,7 +1034,7 @@ private final void updateWimaxState(Intent intent) { updateWimaxIcons(); } - private void updateWimaxIcons() { + protected void updateWimaxIcons() { if (mIsWimaxEnabled) { if (mWimaxConnected) { if (mWimaxIdle) @@ -999,7 +1054,7 @@ private void updateWimaxIcons() { // ===== Full or limited Internet connectivity ================================== - private void updateConnectivity(Intent intent) { + protected void updateConnectivity(Intent intent) { if (CHATTY) { Slog.d(TAG, "updateConnectivity: intent=" + intent); } @@ -1177,6 +1232,7 @@ void refreshViews() { mAirplaneIconId = R.drawable.stat_sys_signal_flightmode; mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0; mQSPhoneSignalIconId = 0; + mNoSimIconId = 0; // combined values from connected wifi take precedence over airplane mode if (mWifiConnected) { @@ -1217,6 +1273,12 @@ void refreshViews() { } } + if (!mAirplaneMode && mSimState == IccCardConstants.State.ABSENT) { + // look again; your radios are now sim cards + mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0; + mQSPhoneSignalIconId = 0; + } + if (DEBUG) { Slog.d(TAG, "refreshViews connected={" + (mWifiConnected?" wifi":"") @@ -1239,6 +1301,7 @@ void refreshViews() { + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId) + + " mNoSimIconId=0x" + Integer.toHexString(mNoSimIconId) + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId) + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); @@ -1252,7 +1315,7 @@ void refreshViews() { || mLastAirplaneMode != mAirplaneMode || mLastLocale != mLocale || mLastEtherIconId != mEtherIconId - || mLastAirplaneMode != mAirplaneMode) + || mLastSimIconId != mNoSimIconId) { // NB: the mLast*s will be updated later for (SignalCluster cluster : mSignalClusters) { @@ -1298,6 +1361,10 @@ void refreshViews() { } } + if (mLastSimIconId != mNoSimIconId) { + mLastSimIconId = mNoSimIconId; + } + // the wifi icon on phones if (mLastWifiIconId != mWifiIconId) { mLastWifiIconId = mWifiIconId; @@ -1557,7 +1624,7 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(""); } - private String getResourceName(int resId) { + protected String getResourceName(int resId) { if (resId != 0) { final Resources res = mContext.getResources(); try { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index f46ff7b084dd0..f8e8d8a082104 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +46,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; +import android.telephony.MSimTelephonyManager; import android.text.TextUtils; import android.util.Pair; import android.util.Slog; @@ -69,6 +73,7 @@ import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DoNotDisturb; +import com.android.systemui.statusbar.MSimSignalClusterView; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.SignalClusterView; @@ -81,6 +86,7 @@ import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NotificationRowLayout; +import com.android.systemui.statusbar.policy.MSimNetworkController; import com.android.systemui.statusbar.policy.Prefs; import java.io.FileDescriptor; @@ -161,6 +167,7 @@ public class TabletStatusBar extends BaseStatusBar implements LocationController mLocationController; NetworkController mNetworkController; DoNotDisturb mDoNotDisturb; + MSimNetworkController mMSimNetworkController; ViewGroup mBarContents; @@ -278,24 +285,41 @@ protected void addPanelWindows() { // mobile and data icons final ImageView mobileRSSI = (ImageView)mNotificationPanel.findViewById(R.id.mobile_signal); - if (mobileRSSI != null) { - mNetworkController.addPhoneSignalIconView(mobileRSSI); - } final ImageView wifiRSSI = (ImageView)mNotificationPanel.findViewById(R.id.wifi_signal); - if (wifiRSSI != null) { - mNetworkController.addWifiIconView(wifiRSSI); - } - mNetworkController.addWifiLabelView( - (TextView)mNotificationPanel.findViewById(R.id.wifi_text)); - - mNetworkController.addDataTypeIconView( - (ImageView)mNotificationPanel.findViewById(R.id.mobile_type)); - mNetworkController.addMobileLabelView( - (TextView)mNotificationPanel.findViewById(R.id.mobile_text)); - mNetworkController.addCombinedLabelView( - (TextView)mBarContents.findViewById(R.id.network_text)); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + if (mobileRSSI != null) { + mMSimNetworkController.addPhoneSignalIconView(mobileRSSI); + } + if (wifiRSSI != null) { + mMSimNetworkController.addWifiIconView(wifiRSSI); + } + mMSimNetworkController.addWifiLabelView( + (TextView)mNotificationPanel.findViewById(R.id.wifi_text)); + + mMSimNetworkController.addDataTypeIconView( + (ImageView)mNotificationPanel.findViewById(R.id.mobile_type)); + mMSimNetworkController.addMobileLabelView( + (TextView)mNotificationPanel.findViewById(R.id.mobile_text)); + mMSimNetworkController.addCombinedLabelView( + (TextView)mBarContents.findViewById(R.id.network_text)); + } else { + if (mobileRSSI != null) { + mNetworkController.addPhoneSignalIconView(mobileRSSI); + } + if (wifiRSSI != null) { + mNetworkController.addWifiIconView(wifiRSSI); + } + mNetworkController.addWifiLabelView( + (TextView)mNotificationPanel.findViewById(R.id.wifi_text)); + mNetworkController.addDataTypeIconView( + (ImageView)mNotificationPanel.findViewById(R.id.mobile_type)); + mNetworkController.addMobileLabelView( + (TextView)mNotificationPanel.findViewById(R.id.mobile_text)); + mNetworkController.addCombinedLabelView( + (TextView)mBarContents.findViewById(R.id.network_text)); + } mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel); WindowManager.LayoutParams lp = mNotificationPanelParams = new WindowManager.LayoutParams( @@ -570,12 +594,24 @@ protected View makeStatusBarView() { mBluetoothController = new BluetoothController(mContext); mBluetoothController.addIconView((ImageView)sb.findViewById(R.id.bluetooth)); - mNetworkController = new NetworkController(mContext); - mSignalView = (SignalClusterView) sb.findViewById(R.id.signal_cluster); - mNetworkController.addSignalCluster(mSignalView); - mClock = (Clock) sb.findViewById(R.id.clock); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + final MSimSignalClusterView mSimSignalCluster = + (MSimSignalClusterView)sb.findViewById(R.id.msim_signal_cluster); + + mMSimNetworkController = new MSimNetworkController(mContext); + for(int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + mMSimNetworkController.addSignalCluster(mSimSignalCluster, i); + } + } else { + final SignalClusterView signalCluster = + (SignalClusterView)sb.findViewById(R.id.signal_cluster); + + mNetworkController = new NetworkController(mContext); + mNetworkController.addSignalCluster(signalCluster); + } + // The navigation buttons mBackButton = (ImageView)sb.findViewById(R.id.back); mNavigationArea = (ViewGroup) sb.findViewById(R.id.navigationArea); @@ -1600,8 +1636,15 @@ public void onReceive(Context context, Intent intent) { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print("mDisabled=0x"); pw.println(Integer.toHexString(mDisabled)); - pw.println("mNetworkController:"); - mNetworkController.dump(fd, pw, args); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + pw.println("mMSimNetworkController:"); + for(int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + mMSimNetworkController.dump(fd, pw, args, i); + } + } else { + pw.println("mNetworkController:"); + mNetworkController.dump(fd, pw, args); + } } @Override diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index c77b31f48faf8..b0dfdf480f5b1 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -159,6 +159,9 @@ public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) { mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean( com.android.internal.R.bool.config_useFixedVolume); + + // set the initial status of airplane mode toggle + mAirplaneState = getUpdatedAirplaneToggleState(); } /** @@ -1209,15 +1212,17 @@ public void handleMessage(Message msg) { } }; + private ToggleAction.State getUpdatedAirplaneToggleState() { + return (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1) ? + ToggleAction.State.On : ToggleAction.State.Off; + } + private void onAirplaneModeChanged() { // Let the service state callbacks handle the state. if (mHasTelephony) return; - boolean airplaneModeOn = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, - 0) == 1; - mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off; + mAirplaneState = getUpdatedAirplaneToggleState(); mAirplaneModeOn.updateState(mAirplaneState); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 900c9ef72c133..7c9bbe7ac175f 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -324,6 +324,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mBackKillTimeout; int mPointerLocationMode = 0; // guarded by mLock int mDeviceHardwareKeys; + boolean mSingleStageCameraKey; boolean mHasMenuKeyEnabled; int mCurrentUser = 0; @@ -338,9 +339,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Behavior of volume wake boolean mVolumeWakeScreen; - // Behavior of volbtn music controls + // Behavior of camera wake + boolean mCameraWakeScreen; + boolean mCameraSleepOnRelease; + boolean mIsFocusPressed; + + // Behavior of volbtn/camera music controls + boolean mCameraMusicControls; boolean mVolBtnMusicControls; boolean mIsLongPress; + boolean mCameraKeyPressable = false; + private boolean mAnimatingWindows; private boolean mNeedUpdateSettings; @@ -599,9 +608,18 @@ void observe() { resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.VOLUME_WAKE_SCREEN), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.CAMERA_WAKE_SCREEN), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.CAMERA_SLEEP_ON_RELEASE), false, this, + UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.VOLBTN_MUSIC_CONTROLS), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.CAMERA_MUSIC_CONTROLS), false, this, + UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.ACCELEROMETER_ROTATION), false, this, UserHandle.USER_ALL); @@ -653,9 +671,6 @@ void observe() { resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION), false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(Settings.System.getUriFor( - Settings.System.HARDWARE_KEY_REBINDING), false, this, - UserHandle.USER_ALL); updateSettings(); } @@ -813,6 +828,19 @@ private void interceptScreenshotChord() { } } + private void interceptScreencastChord() { + if (mVolumeUpKeyTriggered && mPowerKeyTriggered && !mVolumeDownKeyTriggered) { + final long now = SystemClock.uptimeMillis(); + if (now <= mVolumeUpKeyTime + ACTION_CHORD_DEBOUNCE_DELAY_MILLIS + && now <= mPowerKeyTime + ACTION_CHORD_DEBOUNCE_DELAY_MILLIS) { + mVolumeUpKeyConsumedByChord = true; + cancelPendingPowerKeyAction(); + + mHandler.postDelayed(mScreencastRunnable, getScreenshotChordLongPressDelay()); + } + } + } + private long getScreenshotChordLongPressDelay() { if (mKeyguardMediator.isShowing()) { // Double the time it takes to take a screenshot from the keyguard @@ -826,6 +854,10 @@ private void cancelPendingScreenshotChordAction() { mHandler.removeCallbacks(mScreenshotRunnable); } + private void cancelPendingScreencastChordAction() { + mHandler.removeCallbacks(mScreencastRunnable); + } + private void interceptRingerChord() { if (mVolumeDownKeyTriggered && !mPowerKeyTriggered && mVolumeUpKeyTriggered) { final long now = SystemClock.uptimeMillis(); @@ -886,6 +918,14 @@ public void run() { } }; + private final Runnable mScreencastRunnable = new Runnable() { + @Override + public void run() { + Intent screencastIntent = new Intent("com.cyanogenmod.ACTION_START_SCREENCAST"); + mContext.sendBroadcastAsUser(screencastIntent, UserHandle.CURRENT_OR_SELF); + } + }; + private final Runnable mRingerChordLongPress = new Runnable() { public void run() { // Do the switch @@ -974,7 +1014,7 @@ private void performKeyAction(int behavior) { triggerVirtualKeypress(KeyEvent.KEYCODE_SEARCH); break; case KEY_ACTION_LAUNCH_CAMERA: - triggerVirtualKeypress(KeyEvent.KEYCODE_CAMERA); + launchCameraAction(); break; default: break; @@ -1102,6 +1142,8 @@ public void init(Context context, IWindowManager windowManager, com.android.internal.R.integer.config_backKillTimeout); mDeviceHardwareKeys = mContext.getResources().getInteger( com.android.internal.R.integer.config_deviceHardwareKeys); + mSingleStageCameraKey = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_singleStageCameraKey); updateKeyAssignments(); @@ -1168,10 +1210,9 @@ private void updateKeyAssignments() { final boolean hasHome = (mDeviceHardwareKeys & KEY_MASK_HOME) != 0; final boolean hasAssist = (mDeviceHardwareKeys & KEY_MASK_ASSIST) != 0; final boolean hasAppSwitch = (mDeviceHardwareKeys & KEY_MASK_APP_SWITCH) != 0; - final boolean hasCamera = (mDeviceHardwareKeys & KEY_MASK_CAMERA) != 0; final ContentResolver resolver = mContext.getContentResolver(); - // initialize all assignments to sane defaults + // Initialize all assignments to sane defaults. mPressOnMenuBehavior = KEY_ACTION_MENU; if (!hasMenu || hasAssist) { mLongPressOnMenuBehavior = KEY_ACTION_NOTHING; @@ -1186,29 +1227,24 @@ private void updateKeyAssignments() { mLongPressOnHomeBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnHomeBehavior); if (mLongPressOnHomeBehavior < KEY_ACTION_NOTHING || - mLongPressOnHomeBehavior > KEY_ACTION_IN_APP_SEARCH) { + mLongPressOnHomeBehavior > KEY_ACTION_LAUNCH_CAMERA) { mLongPressOnHomeBehavior = KEY_ACTION_NOTHING; } mDoubleTapOnHomeBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_doubleTapOnHomeBehavior); if (mDoubleTapOnHomeBehavior < KEY_ACTION_NOTHING || - mDoubleTapOnHomeBehavior > KEY_ACTION_IN_APP_SEARCH) { + mDoubleTapOnHomeBehavior > KEY_ACTION_LAUNCH_CAMERA) { mDoubleTapOnHomeBehavior = KEY_ACTION_NOTHING; } - boolean keyRebindingEnabled = Settings.System.getIntForUser(resolver, - Settings.System.HARDWARE_KEY_REBINDING, 0, UserHandle.USER_CURRENT) == 1; - if (!keyRebindingEnabled) { - mHasMenuKeyEnabled = hasMenu; - return; - } + mHasMenuKeyEnabled = false; + // Check for custom assignments and whether KEY_ACTION_MENU is assigned. if (hasHome) { mLongPressOnHomeBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_HOME_LONG_PRESS_ACTION, - hasAppSwitch ? KEY_ACTION_NOTHING : KEY_ACTION_APP_SWITCH, - UserHandle.USER_CURRENT); + mLongPressOnHomeBehavior, UserHandle.USER_CURRENT); mDoubleTapOnHomeBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_HOME_DOUBLE_TAP_ACTION, mDoubleTapOnHomeBehavior, UserHandle.USER_CURRENT); @@ -1218,31 +1254,30 @@ private void updateKeyAssignments() { if (hasMenu) { mPressOnMenuBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_MENU_ACTION, - KEY_ACTION_MENU, UserHandle.USER_CURRENT); + mPressOnMenuBehavior, UserHandle.USER_CURRENT); mLongPressOnMenuBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_MENU_LONG_PRESS_ACTION, - hasAssist ? KEY_ACTION_NOTHING : KEY_ACTION_SEARCH, - UserHandle.USER_CURRENT); + mLongPressOnMenuBehavior, UserHandle.USER_CURRENT); mHasMenuKeyEnabled |= mPressOnMenuBehavior == KEY_ACTION_MENU || mLongPressOnMenuBehavior == KEY_ACTION_MENU; } if (hasAssist) { mPressOnAssistBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_ASSIST_ACTION, - KEY_ACTION_SEARCH, UserHandle.USER_CURRENT); + mPressOnAssistBehavior, UserHandle.USER_CURRENT); mLongPressOnAssistBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_ASSIST_LONG_PRESS_ACTION, - KEY_ACTION_VOICE_SEARCH, UserHandle.USER_CURRENT); + mLongPressOnAssistBehavior, UserHandle.USER_CURRENT); mHasMenuKeyEnabled |= mPressOnAssistBehavior == KEY_ACTION_MENU || mLongPressOnAssistBehavior == KEY_ACTION_MENU; } if (hasAppSwitch) { mPressOnAppSwitchBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_APP_SWITCH_ACTION, - KEY_ACTION_APP_SWITCH, UserHandle.USER_CURRENT); + mPressOnAppSwitchBehavior, UserHandle.USER_CURRENT); mLongPressOnAppSwitchBehavior = Settings.System.getIntForUser(resolver, Settings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION, - KEY_ACTION_NOTHING, UserHandle.USER_CURRENT); + mLongPressOnAppSwitchBehavior, UserHandle.USER_CURRENT); mHasMenuKeyEnabled |= mPressOnAppSwitchBehavior == KEY_ACTION_MENU || mLongPressOnAppSwitchBehavior == KEY_ACTION_MENU; } @@ -1393,8 +1428,16 @@ public void updateSettings() { Settings.System.HOME_WAKE_SCREEN, 1, UserHandle.USER_CURRENT) == 1); mVolumeWakeScreen = (Settings.System.getIntForUser(resolver, Settings.System.VOLUME_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1); + mCameraWakeScreen = (Settings.System.getIntForUser(resolver, + Settings.System.CAMERA_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1); + mCameraSleepOnRelease = ((Settings.System.getIntForUser(resolver, + Settings.System.CAMERA_SLEEP_ON_RELEASE, 0, UserHandle.USER_CURRENT) == 1) + && mCameraWakeScreen); mVolBtnMusicControls = (Settings.System.getIntForUser(resolver, Settings.System.VOLBTN_MUSIC_CONTROLS, 1, UserHandle.USER_CURRENT) == 1); + mCameraMusicControls = ((Settings.System.getIntForUser(resolver, + Settings.System.CAMERA_MUSIC_CONTROLS, 1, UserHandle.USER_CURRENT) == 1) + && !mCameraWakeScreen); updateKeyAssignments(); @@ -2272,18 +2315,6 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p mHandler.removeCallbacks(mBackLongPress); } - // Specific device key handling - if (mDeviceKeyHandler != null) { - try { - // The device only should consume known keys. - if (mDeviceKeyHandler.handleKeyEvent(event)) { - return -1; - } - } catch (Exception e) { - Slog.w(TAG, "Could not dispatch event to device key handler", e); - } - } - // First we always handle the home key here, so applications // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second @@ -2381,6 +2412,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p } else if (longPress) { if (!keyguardOn && !mHomeConsumed && mLongPressOnHomeBehavior != KEY_ACTION_NOTHING) { + mHomePressed = true; if (mLongPressOnHomeBehavior != KEY_ACTION_APP_SWITCH) { cancelPreloadRecentApps(); } @@ -2395,7 +2427,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p // Hijack modified menu keys for debugging features final int chordBug = KeyEvent.META_SHIFT_ON; - if (virtualKey) { + if (virtualKey || keyguardOn) { // Let the app handle the key return 0; } @@ -2428,7 +2460,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p return -1; } } else if (longPress) { - if (!keyguardOn && mLongPressOnMenuBehavior != KEY_ACTION_NOTHING) { + if (mLongPressOnMenuBehavior != KEY_ACTION_NOTHING) { if (mLongPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) { cancelPreloadRecentApps(); } @@ -2446,7 +2478,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p if (mPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) { cancelPreloadRecentApps(); } - if (!canceled && !keyguardOn) { + if (!canceled) { performKeyAction(mPressOnMenuBehavior); } } @@ -2778,6 +2810,13 @@ private void launchAssistAction() { } } + private void launchCameraAction() { + sendCloseSystemWindows(); + Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF, + null, null, null, 0, null, null); + } + private SearchManager getSearchManager() { if (mSearchManager == null) { mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); @@ -4188,6 +4227,18 @@ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean i return result; } + // Specific device key handling + if (mDeviceKeyHandler != null) { + try { + // The device only should consume known keys. + if (mDeviceKeyHandler.handleKeyEvent(event)) { + return 0; + } + } catch (Exception e) { + Slog.w(TAG, "Could not dispatch event to device key handler", e); + } + } + // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_ENDCALL: { @@ -4230,6 +4281,52 @@ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean i } } break; + case KeyEvent.KEYCODE_FOCUS: + if (down && !isScreenOn && mCameraWakeScreen) { + if (keyguardActive) { + mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode); + } else { + result |= ACTION_WAKE_UP; + } + if (mCameraSleepOnRelease) { + mIsFocusPressed = true; + } + } else if (!down && isScreenOn && mIsFocusPressed) { + result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; + mIsFocusPressed = false; + } + break; + case KeyEvent.KEYCODE_CAMERA: + if (down && mIsFocusPressed) { + mIsFocusPressed = false; + } + if (down && !isScreenOn && mCameraWakeScreen && mSingleStageCameraKey) { + if (keyguardActive) { + mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode); + } else { + result |= ACTION_WAKE_UP; + } + } + if (mCameraMusicControls) { + // if the camera key is not pressable, see if music is active + if (!mCameraKeyPressable) { + mCameraKeyPressable = isMusicActive(); + } + + if (mCameraKeyPressable) { + if (down) { + Message msg = mHandler.obtainMessage(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK, + new KeyEvent(event.getDownTime(), event.getEventTime(), + event.getAction(), KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0)); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, ViewConfiguration.getLongPressTimeout()); + break; + } else { + mHandler.removeMessages(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK); + } + } + } + break; case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { @@ -4241,6 +4338,7 @@ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean i mVolumeDownKeyTime = event.getDownTime(); mVolumeDownKeyConsumedByChord = false; cancelPendingPowerKeyAction(); + cancelPendingScreencastChordAction(); interceptScreenshotChord(); interceptRingerChord(); } @@ -4248,6 +4346,7 @@ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean i mVolumeDownKeyTriggered = false; cancelPendingScreenshotChordAction(); cancelPendingRingerChordAction(); + cancelPendingScreencastChordAction(); } } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { if (down) { @@ -4259,11 +4358,13 @@ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean i cancelPendingPowerKeyAction(); cancelPendingScreenshotChordAction(); interceptRingerChord(); + interceptScreencastChord(); } } else { mVolumeUpKeyTriggered = false; cancelPendingScreenshotChordAction(); cancelPendingRingerChordAction(); + cancelPendingScreencastChordAction(); } } if (down) { @@ -4335,8 +4436,9 @@ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean i } case KeyEvent.KEYCODE_POWER: { - if ((mTopFullscreenOpaqueWindowState.getAttrs().flags - & WindowManager.LayoutParams.PREVENT_POWER_KEY) != 0){ + if ((mTopFullscreenOpaqueWindowState != null && + (mTopFullscreenOpaqueWindowState.getAttrs().flags + & WindowManager.LayoutParams.PREVENT_POWER_KEY) != 0)) { return result; } result &= ~ACTION_PASS_TO_USER; @@ -4461,7 +4563,7 @@ private boolean isWakeKeyWhenScreenOff(int keyCode) { case KeyEvent.KEYCODE_VOLUME_MUTE: return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED; - // ignore media and camera keys + // ignore media keys case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_PLAY: @@ -4473,10 +4575,11 @@ private boolean isWakeKeyWhenScreenOff(int keyCode) { case KeyEvent.KEYCODE_MEDIA_REWIND: case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - case KeyEvent.KEYCODE_CAMERA: return false; - // home wake can be configurable so default to no here + // home & camera wake can be configurable so default to no here + case KeyEvent.KEYCODE_FOCUS: + case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_HOME: return false; } @@ -4662,6 +4765,11 @@ public void screenTurningOn(final ScreenOnListener screenOnListener) { } synchronized (mLock) { + // since the screen turned on, assume we don't enable play-pause again + // unless they turn it off and music is still playing. this is done to + // prevent the camera button from starting playback if playback wasn't + // originally running + mCameraKeyPressable = false; mScreenOnEarly = true; updateOrientationListenerLp(); updateLockScreenTimeout(); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java index a38e86db39a60..5c7675ec1117a 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -54,13 +57,14 @@ public void onSimStateChanged(IccCardConstants.State simState) { */ private static enum StatusMode { Normal, // Normal case (sim card present, it's not locked) - NetworkLocked, // SIM card is 'network locked'. + PersoLocked, // SIM card is 'perso locked'. SimMissing, // SIM card is missing. SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times SimLocked, // SIM card is currently locked SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure - SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM. + SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM. + SimIOError; //The sim card is faulty } public CarrierText(Context context) { @@ -91,6 +95,9 @@ protected void onFinishInflate() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (KeyguardUpdateMonitor.sIsMultiSimEnabled) { + return; + } KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback); } @@ -109,7 +116,7 @@ protected void onDetachedFromWindow() { * @param spn * @return */ - private CharSequence getCarrierTextForSimState(IccCardConstants.State simState, + protected CharSequence getCarrierTextForSimState(IccCardConstants.State simState, CharSequence plmn, CharSequence spn) { CharSequence carrierText = null; StatusMode status = getStatusForIccState(simState); @@ -122,9 +129,10 @@ private CharSequence getCarrierTextForSimState(IccCardConstants.State simState, carrierText = null; // nothing to display yet. break; - case NetworkLocked: + case PersoLocked: carrierText = makeCarrierStringOnEmergencyCapable( - mContext.getText(R.string.lockscreen_network_locked_message), plmn); + getContext().getText(R.string.lockscreen_perso_locked_message), + plmn); break; case SimMissing: @@ -159,6 +167,12 @@ private CharSequence getCarrierTextForSimState(IccCardConstants.State simState, getContext().getText(R.string.lockscreen_sim_puk_locked_message), plmn); break; + + case SimIOError: + carrierText = makeCarrierStringOnEmergencyCapable( + getContext().getText(R.string.lockscreen_sim_error_message_short), + plmn); + break; } return carrierText; @@ -189,13 +203,13 @@ private StatusMode getStatusForIccState(IccCardConstants.State simState) { && (simState == IccCardConstants.State.ABSENT || simState == IccCardConstants.State.PERM_DISABLED); - // Assume we're NETWORK_LOCKED if not provisioned - simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState; + // Assume we're PERSO_LOCKED if not provisioned + simState = missingAndNotProvisioned ? IccCardConstants.State.PERSO_LOCKED : simState; switch (simState) { case ABSENT: return StatusMode.SimMissing; - case NETWORK_LOCKED: - return StatusMode.SimMissingLocked; + case PERSO_LOCKED: + return StatusMode.PersoLocked; case NOT_READY: return StatusMode.SimNotReady; case PIN_REQUIRED: @@ -208,6 +222,8 @@ private StatusMode getStatusForIccState(IccCardConstants.State simState) { return StatusMode.SimPermDisabled; case UNKNOWN: return StatusMode.SimMissing; + case CARD_IO_ERROR: + return StatusMode.SimIOError; } return StatusMode.SimMissing; } @@ -231,7 +247,7 @@ private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simSta int carrierHelpTextId = 0; StatusMode status = getStatusForIccState(simState); switch (status) { - case NetworkLocked: + case PersoLocked: carrierHelpTextId = R.string.lockscreen_instructions_when_pattern_disabled; break; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index a1ea19af0a3fc..ac69589a45db6 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -52,6 +55,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.telephony.MSimTelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; @@ -60,10 +64,12 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewStub; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.widget.RemoteViews.OnClickHandler; + import com.android.internal.R; import com.android.internal.app.ThemeUtils; import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; @@ -985,6 +991,12 @@ private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { final LayoutInflater inflater = LayoutInflater.from(ThemeUtils.createUiContext(mContext)); if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); View v = inflater.inflate(layoutId, mSecurityViewContainer, false); + if (KeyguardUpdateMonitor.sIsMultiSimEnabled) { + ViewStub vStub = (ViewStub) (v.findViewById(R.id.stub_msim_carrier_text)); + if (vStub != null) { + vStub.inflate(); + } + } mSecurityViewContainer.addView(v); updateSecurityView(v); view = (KeyguardSecurityView)v; @@ -1170,8 +1182,16 @@ private int getSecurityViewIdForMode(SecurityMode securityMode) { case Password: return R.id.keyguard_password_view; case Biometric: return R.id.keyguard_face_unlock_view; case Account: return R.id.keyguard_account_view; - case SimPin: return R.id.keyguard_sim_pin_view; - case SimPuk: return R.id.keyguard_sim_puk_view; + case SimPin: + if (KeyguardUpdateMonitor.sIsMultiSimEnabled) { + return R.id.msim_keyguard_sim_pin_view; + } + return R.id.keyguard_sim_pin_view; + case SimPuk: + if (KeyguardUpdateMonitor.sIsMultiSimEnabled) { + return R.id.msim_keyguard_sim_puk_view; + } + return R.id.keyguard_sim_puk_view; } return 0; } @@ -1184,8 +1204,16 @@ private int getLayoutIdFor(SecurityMode securityMode) { case Password: return R.layout.keyguard_password_view; case Biometric: return R.layout.keyguard_face_unlock_view; case Account: return R.layout.keyguard_account_view; - case SimPin: return R.layout.keyguard_sim_pin_view; - case SimPuk: return R.layout.keyguard_sim_puk_view; + case SimPin: + if (KeyguardUpdateMonitor.sIsMultiSimEnabled) { + return R.layout.msim_keyguard_sim_pin_view; + } + return R.layout.keyguard_sim_pin_view; + case SimPuk: + if (KeyguardUpdateMonitor.sIsMultiSimEnabled) { + return R.layout.msim_keyguard_sim_puk_view; + } + return R.layout.keyguard_sim_puk_view; default: return 0; } @@ -1714,6 +1742,11 @@ private boolean shouldEnableHomeKey() { return homeOverride; } + private boolean shouldEnableCameraKey() { + final boolean cameraOverride = Settings.System.getInt(getContext().getContentResolver(), Settings.System.CAMERA_UNLOCK_SCREEN, 0) == 1; + return cameraOverride; + } + public void goToUserSwitcher() { mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector)); } @@ -1732,6 +1765,16 @@ public boolean handleMenuKey() { return false; } + + public boolean handleCameraKey() { + // The following enables the CAMERA key to work for testing automation + if (shouldEnableCameraKey()) { + showNextSecurityScreenOrFinish(false); + return true; + } + return false; + } + public boolean handleHomeKey() { // The following enables the HOME key to work for testing automation if (shouldEnableHomeKey()) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java index 70909655a8341..d2582e3f07f5b 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -19,6 +22,7 @@ import android.app.ProfileManager; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import com.android.internal.telephony.IccCardConstants; @@ -81,7 +85,20 @@ private boolean isBiometricUnlockSuppressed() { SecurityMode getSecurityMode() { KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - final IccCardConstants.State simState = updateMonitor.getSimState(); + IccCardConstants.State simState = updateMonitor.getSimState(); + + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + for (int i = 0; i < numPhones; i++) { + simState = updateMonitor.getSimState(i); + // We are intereseted only in PIN_REQUIRED or PUK_REQUIRED + // So continue to the next sub if the sim state is other + // than these two. + if (simState == IccCardConstants.State.PIN_REQUIRED + || simState == IccCardConstants.State.PUK_REQUIRED) { + break; + } + } + SecurityMode mode = SecurityMode.None; if (simState == IccCardConstants.State.PIN_REQUIRED) { mode = SecurityMode.SimPin; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java index ab364ee2dc9b2..8bf54d244c9f0 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -17,6 +20,7 @@ package com.android.internal.policy.impl.keyguard; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.PhoneConstants; import android.content.Context; import android.app.Activity; @@ -30,6 +34,7 @@ import android.text.method.DigitsKeyListener; import android.util.AttributeSet; import android.view.View; +import android.util.Log; import android.view.WindowManager; import android.widget.TextView.OnEditorActionListener; @@ -41,8 +46,8 @@ public class KeyguardSimPinView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { - private ProgressDialog mSimUnlockProgressDialog = null; - private volatile boolean mSimCheckInProgress; + protected ProgressDialog mSimUnlockProgressDialog = null; + protected volatile boolean mSimCheckInProgress; public KeyguardSimPinView(Context context) { this(context, null); @@ -52,8 +57,33 @@ public KeyguardSimPinView(Context context, AttributeSet attrs) { super(context, attrs); } + protected void showCancelButton() { + final View cancel = findViewById(R.id.key_cancel); + if (cancel != null) { + cancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doHapticKeyClick(); + } + }); + } + } + public void resetState() { - mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true); + String displayMessage = ""; + try { + int attemptsRemaining = ITelephony.Stub.asInterface(ServiceManager + .checkService("phone")).getIccPin1RetryCount(); + if (attemptsRemaining >= 0) { + displayMessage = getContext().getString(R.string.keyguard_password_wrong_pin_code) + + getContext().getString(R.string.pinpuk_attempts) + + attemptsRemaining + ". "; + } + } catch (RemoteException ex) { + displayMessage = getContext().getString(R.string.keyguard_password_pin_failed); + } + displayMessage = displayMessage + getContext().getString(R.string.kg_sim_pin_instructions) ; + mSecurityMessageDisplay.setMessage(displayMessage, true); mPasswordEntry.setEnabled(true); } @@ -76,6 +106,7 @@ public void onClick(View v) { } }); } + showCancelButton(); // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts, // not a separate view @@ -131,13 +162,13 @@ protected CheckSimPin(String pin) { mPin = pin; } - abstract void onSimCheckResponse(boolean success); + abstract void onSimCheckResponse(final int result); @Override public void run() { try { - final boolean result = ITelephony.Stub.asInterface(ServiceManager - .checkService("phone")).supplyPin(mPin); + final int result = ITelephony.Stub.asInterface(ServiceManager + .checkService("phone")).supplyPinReportResult(mPin); post(new Runnable() { public void run() { onSimCheckResponse(result); @@ -146,14 +177,14 @@ public void run() { } catch (RemoteException e) { post(new Runnable() { public void run() { - onSimCheckResponse(false); + onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE); } }); } } } - private Dialog getSimUnlockProgressDialog() { + protected Dialog getSimUnlockProgressDialog() { if (mSimUnlockProgressDialog == null) { mSimUnlockProgressDialog = new ProgressDialog(mContext); mSimUnlockProgressDialog.setMessage( @@ -185,20 +216,23 @@ protected void verifyPasswordAndUnlock() { if (!mSimCheckInProgress) { mSimCheckInProgress = true; // there should be only one new CheckSimPin(mPasswordEntry.getText().toString()) { - void onSimCheckResponse(final boolean success) { + void onSimCheckResponse(final int result) { post(new Runnable() { public void run() { if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.hide(); } - if (success) { - // before closing the keyguard, report back that the sim is unlocked - // so it knows right away. + if (result == PhoneConstants.PIN_RESULT_SUCCESS) { KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(); mCallback.dismiss(true); } else { - mSecurityMessageDisplay.setMessage - (R.string.kg_password_wrong_pin_code, true); + if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { + mSecurityMessageDisplay.setMessage + (R.string.kg_password_wrong_pin_code, true); + } else { + mSecurityMessageDisplay.setMessage + (R.string.keyguard_password_pin_failed, true); + } mPasswordEntry.setText(""); } mCallback.userActivity(0); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java index e5b4b7308a674..cac43f4c7b6b3 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -31,6 +34,7 @@ import android.widget.TextView.OnEditorActionListener; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.PhoneConstants; import com.android.internal.R; @@ -40,18 +44,18 @@ public class KeyguardSimPukView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { - private ProgressDialog mSimUnlockProgressDialog = null; - private volatile boolean mCheckInProgress; - private String mPukText; - private String mPinText; - private StateMachine mStateMachine = new StateMachine(); + protected ProgressDialog mSimUnlockProgressDialog = null; + protected volatile boolean mCheckInProgress; + protected String mPukText; + protected String mPinText; + protected StateMachine mStateMachine = new StateMachine(); - private class StateMachine { + protected class StateMachine { final int ENTER_PUK = 0; final int ENTER_PIN = 1; final int CONFIRM_PIN = 2; final int DONE = 3; - private int state = ENTER_PUK; + protected int state = ENTER_PUK; public void next() { int msg = 0; @@ -87,10 +91,26 @@ public void next() { } void reset() { + String displayMessage = ""; + try { + int attemptsRemaining = ITelephony.Stub.asInterface(ServiceManager + .checkService("phone")).getIccPin1RetryCount(); + if (attemptsRemaining >= 0) { + displayMessage = getContext().getString( + R.string.keyguard_password_wrong_puk_code) + + getContext().getString(R.string.pinpuk_attempts) + + attemptsRemaining + ". "; + } + } catch (RemoteException ex) { + displayMessage = getContext().getString( + R.string.keyguard_password_puk_failed); + } + displayMessage = displayMessage + + getContext().getString(R.string.kg_puk_enter_puk_hint); mPinText=""; mPukText=""; state = ENTER_PUK; - mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true); + mSecurityMessageDisplay.setMessage(displayMessage, true); mPasswordEntry.requestFocus(); } } @@ -186,13 +206,13 @@ protected CheckSimPuk(String puk, String pin) { mPin = pin; } - abstract void onSimLockChangedResponse(boolean success); + abstract void onSimLockChangedResponse(final int result); @Override public void run() { try { - final boolean result = ITelephony.Stub.asInterface(ServiceManager - .checkService("phone")).supplyPuk(mPuk, mPin); + final int result = ITelephony.Stub.asInterface(ServiceManager + .checkService("phone")).supplyPukReportResult(mPuk, mPin); post(new Runnable() { public void run() { @@ -202,14 +222,14 @@ public void run() { } catch (RemoteException e) { post(new Runnable() { public void run() { - onSimLockChangedResponse(false); + onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE); } }); } } } - private Dialog getSimUnlockProgressDialog() { + protected Dialog getSimUnlockProgressDialog() { if (mSimUnlockProgressDialog == null) { mSimUnlockProgressDialog = new ProgressDialog(mContext); mSimUnlockProgressDialog.setMessage( @@ -224,7 +244,7 @@ private Dialog getSimUnlockProgressDialog() { return mSimUnlockProgressDialog; } - private boolean checkPuk() { + protected boolean checkPuk() { // make sure the puk is at least 8 digits long. if (mPasswordEntry.getText().length() >= 8) { mPukText = mPasswordEntry.getText().toString(); @@ -233,7 +253,7 @@ private boolean checkPuk() { return false; } - private boolean checkPin() { + protected boolean checkPin() { // make sure the PIN is between 4 and 8 digits int length = mPasswordEntry.getText().length(); if (length >= 4 && length <= 8) { @@ -247,23 +267,29 @@ public boolean confirmPin() { return mPinText.equals(mPasswordEntry.getText().toString()); } - private void updateSim() { + protected void updateSim() { getSimUnlockProgressDialog().show(); if (!mCheckInProgress) { mCheckInProgress = true; new CheckSimPuk(mPukText, mPinText) { - void onSimLockChangedResponse(final boolean success) { + void onSimLockChangedResponse(final int result) { post(new Runnable() { public void run() { if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.hide(); } - if (success) { + if (result == PhoneConstants.PIN_RESULT_SUCCESS) { mCallback.dismiss(true); } else { + if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { + mSecurityMessageDisplay.setMessage + (R.string.kg_invalid_puk, true); + } else { + mSecurityMessageDisplay.setMessage + (R.string.keyguard_password_puk_failed, true); + } mStateMachine.reset(); - mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true); } mCheckInProgress = false; } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java index 1715f3a2b59d6..33f1717f191f9 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java @@ -1,5 +1,7 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,8 +48,10 @@ import android.provider.Settings; import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.MSimConstants; import com.android.internal.telephony.TelephonyIntents; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.R; @@ -99,9 +103,9 @@ public class KeyguardUpdateMonitor { private final Context mContext; // Telephony state - private IccCardConstants.State mSimState = IccCardConstants.State.READY; - private CharSequence mTelephonyPlmn; - private CharSequence mTelephonySpn; + private IccCardConstants.State []mSimState; + private CharSequence []mTelephonyPlmn; + private CharSequence []mTelephonySpn; private int mRingMode; private int mPhoneState; private boolean mKeyguardIsVisible; @@ -127,6 +131,9 @@ public class KeyguardUpdateMonitor { private boolean mSwitchingUser; + public static final boolean + sIsMultiSimEnabled = MSimTelephonyManager.getDefault().isMultiSimEnabled(); + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -138,7 +145,7 @@ public void handleMessage(Message msg) { handleBatteryUpdate((BatteryStatus) msg.obj); break; case MSG_CARRIER_INFO_UPDATE: - handleCarrierInfoUpdate(); + handleCarrierInfoUpdate(msg.arg1); break; case MSG_SIM_STATE_CHANGE: handleSimStateChange((SimArgs) msg.obj); @@ -248,9 +255,15 @@ public void onReceive(Context context, Intent intent) { || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE)); } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) { - mTelephonyPlmn = getTelephonyPlmnFrom(intent); - mTelephonySpn = getTelephonySpnFrom(intent); - mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE)); + // Get the subscription from the intent. + final int subscription = intent.getIntExtra(MSimConstants.SUBSCRIPTION_KEY, 0); + Log.d(TAG, "Received SPN update on sub :" + subscription); + // Update PLMN and SPN for corresponding subscriptions. + mTelephonyPlmn[subscription] = getTelephonyPlmnFrom(intent); + mTelephonySpn[subscription] = getTelephonySpnFrom(intent); + final Message msg = mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE); + msg.arg1 = subscription; + mHandler.sendMessage(msg); } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0); @@ -304,16 +317,22 @@ public void onReceive(Context context, Intent intent) { */ private static class SimArgs { public final IccCardConstants.State simState; + public int subscription; - SimArgs(IccCardConstants.State state) { + SimArgs(IccCardConstants.State state, int sub) { simState = state; + subscription = sub; } static SimArgs fromIntent(Intent intent) { IccCardConstants.State state; + int subscription; if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); } + subscription = intent.getIntExtra(MSimConstants.SUBSCRIPTION_KEY, 0); + Log.d(TAG,"ACTION_SIM_STATE_CHANGED intent received on sub = " + subscription); + String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { final String absentReason = intent @@ -334,11 +353,13 @@ static SimArgs fromIntent(Intent intent) { state = IccCardConstants.State.PIN_REQUIRED; } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { state = IccCardConstants.State.PUK_REQUIRED; + } else if (IccCardConstants.INTENT_VALUE_LOCKED_PERSO.equals(lockedReason)) { + state = IccCardConstants.State.PERSO_LOCKED; } else { state = IccCardConstants.State.UNKNOWN; } - } else if (IccCardConstants.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) { - state = IccCardConstants.State.NETWORK_LOCKED; + } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { + state = IccCardConstants.State.CARD_IO_ERROR; } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(stateExtra) || IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(stateExtra)) { // This is required because telephony doesn't return to "READY" after @@ -347,7 +368,7 @@ static SimArgs fromIntent(Intent intent) { } else { state = IccCardConstants.State.UNKNOWN; } - return new SimArgs(state); + return new SimArgs(state, subscription); } public String toString() { @@ -456,9 +477,19 @@ private KeyguardUpdateMonitor(Context context) { } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mSimState = IccCardConstants.State.NOT_READY; mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0); - mTelephonyPlmn = getDefaultPlmn(); + // MSimTelephonyManager.getDefault().getPhoneCount() returns '1' for single SIM + // mode and '2' for dual SIM mode. + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + // Initialize PLMN, SPN strings and SIM states for the subscriptions. + mTelephonyPlmn = new CharSequence[numPhones]; + mTelephonySpn = new CharSequence[numPhones]; + mSimState = new IccCardConstants.State[numPhones]; + for (int i = 0; i < numPhones; i++) { + mTelephonyPlmn[i] = getDefaultPlmn(); + mTelephonySpn[i] = null; + mSimState[i] = IccCardConstants.State.NOT_READY; + } // Watch for interesting updates final IntentFilter filter = new IntentFilter(); @@ -697,14 +728,20 @@ private void handleBatteryUpdate(BatteryStatus status) { /** * Handle {@link #MSG_CARRIER_INFO_UPDATE} */ - private void handleCarrierInfoUpdate() { - if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn - + ", spn = " + mTelephonySpn); + private void handleCarrierInfoUpdate(int subscription) { + if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn[subscription] + + ", spn = " + mTelephonySpn[subscription] + ", subscription = " + subscription); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); + if (sIsMultiSimEnabled) { + cb.onRefreshCarrierInfo(mTelephonyPlmn[subscription], + mTelephonySpn[subscription], subscription); + } else { + cb.onRefreshCarrierInfo(mTelephonyPlmn[subscription], + mTelephonySpn[subscription]); + } } } } @@ -714,18 +751,25 @@ private void handleCarrierInfoUpdate() { */ private void handleSimStateChange(SimArgs simArgs) { final IccCardConstants.State state = simArgs.simState; + final int subscription = simArgs.subscription; - if (DEBUG) { - Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " " - + "state resolved to " + state.toString()); - } + Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " " + + "state resolved to " + state.toString() + " " + + "subscription =" + subscription); - if (state != IccCardConstants.State.UNKNOWN && state != mSimState) { - mSimState = state; + if (state != IccCardConstants.State.UNKNOWN && state != mSimState[subscription]) { + if (DEBUG_SIM_STATES) Log.v(TAG, "dispatching state: " + state + + "subscription: " + subscription); + + mSimState[subscription] = state; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onSimStateChanged(state); + if (sIsMultiSimEnabled) { + cb.onSimStateChanged(state, subscription); + } else { + cb.onSimStateChanged(state); + } } } } @@ -877,9 +921,18 @@ private void sendUpdates(KeyguardUpdateMonitorCallback callback) { callback.onTimeChanged(); callback.onRingerModeChanged(mRingMode); callback.onPhoneStateChanged(mPhoneState); - callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); callback.onClockVisibilityChanged(); - callback.onSimStateChanged(mSimState); + int subscription = MSimTelephonyManager.getDefault().getDefaultSubscription(); + if (sIsMultiSimEnabled) { + for (int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + callback.onRefreshCarrierInfo(mTelephonyPlmn[i], mTelephonySpn[i], i); + callback.onSimStateChanged(mSimState[i], i); + } + } else { + callback.onRefreshCarrierInfo(mTelephonyPlmn[subscription], + mTelephonySpn[subscription]); + callback.onSimStateChanged(mSimState[subscription]); + } callback.onMusicClientIdChanged( mDisplayClientState.clientGeneration, mDisplayClientState.clearing, @@ -901,7 +954,33 @@ public void reportClockVisible(boolean visible) { } public IccCardConstants.State getSimState() { - return mSimState; + return getSimState(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + public IccCardConstants.State getSimState(int subscription) { + return mSimState[subscription]; + } + + public int getPinLockedSubscription() { + int sub = -1; + for (int i = 0; i < mSimState.length; i++) { + if (mSimState[i] == IccCardConstants.State.PIN_REQUIRED) { + sub = i; + break; + } + } + return sub; + } + + public int getPukLockedSubscription() { + int sub = -1; + for (int i = 0; i < mSimState.length; i++) { + if (mSimState[i] == IccCardConstants.State.PUK_REQUIRED) { + sub = i; + break; + } + } + return sub; } /** @@ -913,15 +992,29 @@ public IccCardConstants.State getSimState() { * through mHandler, this *must* be called from the UI thread. */ public void reportSimUnlocked() { - handleSimStateChange(new SimArgs(IccCardConstants.State.READY)); + reportSimUnlocked(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + public void reportSimUnlocked(int subscription) { + if (DEBUG) Log.d(TAG, "reportSimUnlocked(" + subscription + ")"); + mSimState[subscription] = IccCardConstants.State.READY; + handleSimStateChange(new SimArgs(mSimState[subscription], subscription)); } public CharSequence getTelephonyPlmn() { - return mTelephonyPlmn; + return getTelephonyPlmn(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + public CharSequence getTelephonyPlmn(int subscription) { + return mTelephonyPlmn[subscription]; } public CharSequence getTelephonySpn() { - return mTelephonySpn; + return getTelephonySpn(MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + public CharSequence getTelephonySpn(int subscription) { + return mTelephonySpn[subscription]; } /** @@ -970,7 +1063,18 @@ public void setAlternateUnlockEnabled(boolean enabled) { } public boolean isSimLocked() { - return isSimLocked(mSimState); + boolean isLocked = false; + for (IccCardConstants.State simState : mSimState) { + if (isSimLocked(simState)) { + isLocked = true; + break; + } + } + return isLocked; + } + + public boolean isSimLocked(int subscription) { + return isSimLocked(mSimState[subscription]); } public static boolean isSimLocked(IccCardConstants.State state) { @@ -980,7 +1084,17 @@ public static boolean isSimLocked(IccCardConstants.State state) { } public boolean isSimPinSecure() { - return isSimPinSecure(mSimState); + boolean isSecure = false; + for (IccCardConstants.State simState : mSimState) { + if (isSimPinSecure(simState)) { + isSecure = true; + break; + } + } + return isSecure; + } + public boolean isSimPinSecure(int subscription) { + return isSimPinSecure(mSimState[subscription]); } public static boolean isSimPinSecure(IccCardConstants.State state) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java index 41816db0edc8b..7eadf531fea84 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -131,4 +134,20 @@ void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingInten */ public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { } + /** + * Called when the SIM state changes on subscription. + * @param simState + * @param subscription + */ + void onSimStateChanged(IccCardConstants.State simState, int subscription) { } + + /** + * Called when the carrier PLMN or SPN changes. + * + * @param plmn The operator name of the registered network. May be null if it shouldn't + * be displayed. + * @param spn The service provider name. May be null if it shouldn't be displayed. + * @param subscription The subscription for which onRefreshCarrierInfo is meant + */ + void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int subscription) { } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java index fe9288aefa184..ce7407c9f5c9a 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -181,6 +181,14 @@ public boolean dispatchKeyEvent(KeyEvent event) { public boolean handleKeyDown(int keyCode, KeyEvent event) { if (event.getRepeatCount() == 0) { mUnlockKeyDown = true; + // We check for Camera key press in handleKeyDown, because + // it gives us "instant" unlock, when user depresses + // the button. + if (keyCode == KeyEvent.KEYCODE_CAMERA) { + if (mKeyguardView.handleCameraKey()) { + return true; + } + } } if (event.isLongPress()) { String action = null; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java index 94dd96a3ea6ea..b9e590e5c6c10 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2007 The Android Open Source Project * + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -44,6 +47,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.telephony.MSimTelephonyManager; import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; @@ -52,9 +56,9 @@ import android.view.WindowManagerPolicy; import com.android.internal.telephony.IccCardConstants; +import com.android.internal.util.cm.QuietHoursUtils; import com.android.internal.widget.LockPatternUtils; - /** * Mediates requests related to the keyguard. This includes queries about the * state of the keyguard, power management events that effect whether the keyguard @@ -382,6 +386,11 @@ public void onDeviceProvisioned() { @Override public void onSimStateChanged(IccCardConstants.State simState) { + onSimStateChanged(simState, MSimTelephonyManager.getDefault().getDefaultSubscription()); + } + + @Override + public void onSimStateChanged(IccCardConstants.State simState, int subscription) { if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState); switch (simState) { @@ -916,14 +925,15 @@ private void doKeyguardLocked(Bundle options) { } // if the setup wizard hasn't run yet, don't show - final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", - false); final boolean provisioned = mUpdateMonitor.isDeviceProvisioned(); - final IccCardConstants.State state = mUpdateMonitor.getSimState(); - final boolean lockedOrMissing = state.isPinLocked() - || ((state == IccCardConstants.State.ABSENT - || state == IccCardConstants.State.PERM_DISABLED) - && requireSim); + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + final IccCardConstants.State []state = new IccCardConstants.State[numPhones]; + boolean lockedOrMissing = false; + for (int i = 0; i < numPhones; i++) { + state[i] = mUpdateMonitor.getSimState(i); + lockedOrMissing = lockedOrMissing || isLockedOrMissing(state[i]); + if (lockedOrMissing) break; + } if (!lockedOrMissing && !provisioned) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned" @@ -941,6 +951,15 @@ private void doKeyguardLocked(Bundle options) { showLocked(options); } + boolean isLockedOrMissing(IccCardConstants.State state) { + final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", + false); + return (state.isPinLocked() + || ((state == IccCardConstants.State.ABSENT + || state == IccCardConstants.State.PERM_DISABLED) + && requireSim)); + } + /** * Dismiss the keyguard through the security layers. */ @@ -1239,6 +1258,10 @@ private void playSounds(boolean locked) { return; } + if (QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_SYSTEM)) { + return; + } + final ContentResolver cr = mContext.getContentResolver(); if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) { final int whichSound = locked diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MSimCarrierText.java b/policy/src/com/android/internal/policy/impl/keyguard/MSimCarrierText.java new file mode 100644 index 0000000000000..212a34b8b51de --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/MSimCarrierText.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * 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.policy.impl.keyguard; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.TextView; + +import android.util.Log; + +import com.android.internal.R; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.IccCardConstants.State; +import com.android.internal.widget.LockPatternUtils; + +import android.telephony.MSimTelephonyManager; + +public class MSimCarrierText extends CarrierText { + private static final String TAG = "MSimCarrierText"; + private CharSequence []mPlmn; + private CharSequence []mSpn; + private State []mSimState; + + private KeyguardUpdateMonitorCallback mMSimCallback = new KeyguardUpdateMonitorCallback() { + + @Override + public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int sub) { + mPlmn[sub] = plmn; + mSpn[sub] = spn; + updateCarrierText(mSimState, mPlmn, mSpn); + } + + @Override + public void onSimStateChanged(IccCardConstants.State simState, int sub) { + mSimState[sub] = simState; + updateCarrierText(mSimState, mPlmn, mSpn); + } + }; + + private void initialize() { + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + mPlmn = new CharSequence[numPhones]; + mSpn = new CharSequence[numPhones]; + mSimState = new State[numPhones]; + } + + public MSimCarrierText(Context context) { + this(context, null); + } + + public MSimCarrierText(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(); + } + + protected void updateCarrierText(State []simState, CharSequence []plmn, CharSequence []spn) { + CharSequence text = ""; + for (int i = 0; i < simState.length; i++) { + CharSequence displayText = getCarrierTextForSimState(simState[i], plmn[i], spn[i]); + if (KeyguardViewManager.USE_UPPER_CASE) { + displayText = (displayText != null ? displayText.toString().toUpperCase() : ""); + } + text = (TextUtils.isEmpty(text) + ? displayText + : getContext().getString(R.string.msim_carrier_text_format, text, displayText)); + } + Log.d(TAG, "updateCarrierText: text = " + text); + setText(text); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mMSimCallback); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mMSimCallback); + } +} + diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MSimKeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/MSimKeyguardSimPinView.java new file mode 100644 index 0000000000000..d20d2b2e3c9f9 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/MSimKeyguardSimPinView.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * 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.policy.impl.keyguard; + +import com.android.internal.telephony.msim.ITelephonyMSim; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.telephony.MSimTelephonyManager; +import android.util.AttributeSet; +import android.view.View; +import android.util.Log; + +import com.android.internal.R; +import com.android.internal.telephony.IccCardConstants; + +/** + * Displays a PIN pad for unlocking. + */ +public class MSimKeyguardSimPinView extends KeyguardSimPinView { + private final static boolean DEBUG = KeyguardViewMediator.DEBUG; + private static String TAG = "MSimKeyguardSimPinView"; + private static int sCancelledCount = 0; + private int mSubscription = -1; + + public MSimKeyguardSimPinView(Context context) { + this(context, null); + } + + public MSimKeyguardSimPinView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void showCancelButton() { + final View cancel = findViewById(R.id.key_cancel); + if (cancel != null) { + cancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doHapticKeyClick(); + closeKeyGuard(false); + } + }); + } + } + + /* + * CloseKeyGuard: Method used to Close the keyguard. + * @param bAuthenticated: + * true: user entered correct PIN and pressed OK. + * false: user pressed cancel. + * + * Description: used to close keyguard when user presses cancel or + * user enters correct PIN and presses OK + * + * Logic: + * 1. Calculate number of SIMs configured i.e. selected from MultiSIM settings + * 2. Calculate number of SIMs which are PIN Locked + * 3. sCancelledCount will tell us number of keyguards got cancelled. If user tries to cancel + * all Keyguard screens, do not allow him to close Keyguard as atleast one SIM has to + * be authenticated before allowing user to access HomeScreen. + * 4. If present keyguard screen is last one we are about to show HomeScreen after closing this + * keyguard so reset the sCancelledCount. + * 5. Report SIM unlocked to KGUpdateMonitor so that keyguard is closed right away without + * waiting for indication from framework. + */ + private void closeKeyGuard(boolean bAuthenticated) { + KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(getContext()); + int numCardsConfigured = 0; + int numPinLocked = 0; + IccCardConstants.State simState = IccCardConstants.State.UNKNOWN; + + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + for (int i = 0; i < numPhones; i++) { + simState = updateMonitor.getSimState(i); + if (simState == IccCardConstants.State.PIN_REQUIRED) { + numPinLocked++; + } + + //Get number of cards that are present and configured. + //Exclude cards with DETECTED state as they are not configured and are not used. + if (simState == IccCardConstants.State.READY || + simState == IccCardConstants.State.PIN_REQUIRED || + simState == IccCardConstants.State.PUK_REQUIRED || + simState == IccCardConstants.State.PERSO_LOCKED) { + numCardsConfigured++; + } + } + + //If Cancel is pressed for last configured sub return without incrementing sCancelledCount + // else increment sCancelledCount. + if (!bAuthenticated) { + if (MSimKeyguardSimPinView.sCancelledCount >= (numCardsConfigured - 1)) { + return; + } else { + MSimKeyguardSimPinView.sCancelledCount++; + } + } + + //If this is last PIN lock screen, we will show HomeScreen now. + //Hence reset the static sCancelledCount variable. + if (numPinLocked <= 1) { + MSimKeyguardSimPinView.sCancelledCount = 0; + } + + //If Cancel is pressed get current locked sub, + //In case user presses OK we anyways have locked sub, no need to get again. + if (!bAuthenticated) mSubscription = updateMonitor.getPinLockedSubscription(); + + //Before closing the keyguard, report back that the sim is unlocked + //so it knows right away. + if (mSubscription >= 0) updateMonitor.reportSimUnlocked(mSubscription); + mCallback.dismiss(true); + } + + public void resetState() { + String displayMessage = ""; + try { + mSubscription = KeyguardUpdateMonitor.getInstance(mContext) + .getPinLockedSubscription(); + int attemptsRemaining = ITelephonyMSim.Stub.asInterface(ServiceManager + .checkService("phone_msim")).getIccPin1RetryCount(mSubscription); + + if (attemptsRemaining >= 0) { + displayMessage = getContext().getString(R.string.keyguard_password_wrong_pin_code) + + getContext().getString(R.string.pinpuk_attempts) + + attemptsRemaining + ". "; + } + } catch (RemoteException ex) { + displayMessage = getContext().getString(R.string.keyguard_password_pin_failed); + } + displayMessage = getSecurityMessageDisplay(R.string.kg_sim_pin_instructions) + + displayMessage; + mSecurityMessageDisplay.setMessage(displayMessage, true); + mPasswordEntry.setEnabled(true); + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class MSimCheckSimPin extends Thread { + private final String mPin; + + protected MSimCheckSimPin(String pin, int sub) { + mPin = pin; + mSubscription = sub; + } + + abstract void onSimCheckResponse(boolean success); + + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "MSimCheckSimPin:run(), mPin = " + mPin + + " mSubscription = " + mSubscription); + final boolean result = ITelephonyMSim.Stub.asInterface(ServiceManager + .checkService("phone_msim")).supplyPin(mPin, mSubscription); + post(new Runnable() { + public void run() { + onSimCheckResponse(result); + } + }); + } catch (RemoteException e) { + post(new Runnable() { + public void run() { + onSimCheckResponse(false); + } + }); + } + } + } + + @Override + protected void verifyPasswordAndUnlock() { + String entry = mPasswordEntry.getText().toString(); + if (DEBUG) Log.d(TAG, "verifyPasswordAndUnlock(): entry = " + entry); + + if (entry.length() < 4) { + // otherwise, display a message to the user, and don't submit. + mSecurityMessageDisplay.setMessage( + getSecurityMessageDisplay(R.string.kg_invalid_sim_pin_hint), true); + mPasswordEntry.setText(""); + mCallback.userActivity(0); + return; + } + + getSimUnlockProgressDialog().show(); + + if (!mSimCheckInProgress) { + mSimCheckInProgress = true; // there should be only one + + if (DEBUG) Log.d(TAG, "startCheckSimPin(), Multi SIM enabled"); + new MSimCheckSimPin(mPasswordEntry.getText().toString(), + KeyguardUpdateMonitor.getInstance(mContext).getPinLockedSubscription()) { + void onSimCheckResponse(final boolean success) { + post(new Runnable() { + public void run() { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + if (success) { + // before closing the keyguard, report back that the sim is unlocked + // so it knows right away. + closeKeyGuard(success); + } else { + mSecurityMessageDisplay.setMessage(getSecurityMessageDisplay + (R.string.kg_password_wrong_pin_code), true); + mPasswordEntry.setText(""); + } + mCallback.userActivity(0); + mSimCheckInProgress = false; + } + }); + } + }.start(); + } + } + + protected CharSequence getSecurityMessageDisplay(int resId) { + // Returns the String in the format + // "SUB:%d : %s", sub, msg + return getContext().getString(R.string.msim_kg_sim_pin_msg_format, + KeyguardUpdateMonitor.getInstance(mContext).getPinLockedSubscription()+1, + getContext().getResources().getText(resId)); + } +} + diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MSimKeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/MSimKeyguardSimPukView.java new file mode 100644 index 0000000000000..82712ab475e6d --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/MSimKeyguardSimPukView.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * 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.policy.impl.keyguard; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.AttributeSet; +import android.util.Log; + +import com.android.internal.telephony.msim.ITelephonyMSim; + +import com.android.internal.R; + +/** + * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. + */ +public class MSimKeyguardSimPukView extends KeyguardSimPukView { + private final static boolean DEBUG = KeyguardViewMediator.DEBUG; + private static String TAG = "MSimKeyguardSimPukView"; + + protected class MSimStateMachine extends KeyguardSimPukView.StateMachine { + public void next() { + int msg = 0; + if (state == ENTER_PUK) { + if (checkPuk()) { + state = ENTER_PIN; + msg = R.string.kg_puk_enter_pin_hint; + } else { + msg = R.string.kg_invalid_sim_puk_hint; + } + } else if (state == ENTER_PIN) { + if (checkPin()) { + state = CONFIRM_PIN; + msg = R.string.kg_enter_confirm_pin_hint; + } else { + msg = R.string.kg_invalid_sim_pin_hint; + } + } else if (state == CONFIRM_PIN) { + if (confirmPin()) { + state = DONE; + msg = + com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message; + updateSim(); + } else { + state = ENTER_PIN; // try again? + msg = R.string.kg_invalid_confirm_pin_hint; + } + } + mPasswordEntry.setText(null); + if (msg != 0) { + mSecurityMessageDisplay.setMessage(getSecurityMessageDisplay(msg), true); + } + } + + void reset() { + String displayMessage = ""; + try { + int attemptsRemaining = ITelephonyMSim.Stub.asInterface(ServiceManager + .checkService("phone_msim")).getIccPin1RetryCount(KeyguardUpdateMonitor + .getInstance(mContext).getPukLockedSubscription()); + if (attemptsRemaining >= 0) { + displayMessage = getContext().getString( + R.string.keyguard_password_wrong_puk_code) + + getContext().getString(R.string.pinpuk_attempts) + + attemptsRemaining + ". "; + } + } catch (RemoteException ex) { + displayMessage = getContext().getString( + R.string.keyguard_password_puk_failed); + } + displayMessage = getSecurityMessageDisplay(R.string.kg_puk_enter_puk_hint) + + displayMessage; + mPinText=""; + mPukText=""; + state = ENTER_PUK; + mSecurityMessageDisplay.setMessage(displayMessage, true); + mPasswordEntry.requestFocus(); + } + } + + public MSimKeyguardSimPukView(Context context) { + this(context, null); + } + + public MSimKeyguardSimPukView(Context context, AttributeSet attrs) { + super(context, attrs); + mStateMachine = new MSimStateMachine(); + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class MSimCheckSimPuk extends Thread { + private final String mPin, mPuk; + protected final int mSubscription; + + protected MSimCheckSimPuk(String puk, String pin, int sub) { + mPuk = puk; + mPin = pin; + mSubscription = sub; + } + + abstract void onSimLockChangedResponse(boolean success); + + @Override + public void run() { + if (DEBUG) Log.d(TAG, "MSimCheckSimPuk:run(), mPuk = " + mPuk + + " mPin = " + mPin + " mSubscription = " + mSubscription); + try { + final boolean result = ITelephonyMSim.Stub.asInterface(ServiceManager + .checkService("phone_msim")).supplyPuk(mPuk, mPin, mSubscription); + + post(new Runnable() { + public void run() { + onSimLockChangedResponse(result); + } + }); + } catch (RemoteException e) { + post(new Runnable() { + public void run() { + onSimLockChangedResponse(false); + } + }); + } + } + } + + @Override + protected void updateSim() { + getSimUnlockProgressDialog().show(); + + if (DEBUG) Log.d(TAG, "updateSim()"); + + if (!mCheckInProgress) { + mCheckInProgress = true; + + new MSimCheckSimPuk(mPukText, mPinText, + KeyguardUpdateMonitor.getInstance(mContext).getPukLockedSubscription()) { + void onSimLockChangedResponse(final boolean success) { + post(new Runnable() { + public void run() { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + if (success) { + mCallback.dismiss(true); + } else { + mStateMachine.reset(); + mSecurityMessageDisplay.setMessage( + getSecurityMessageDisplay(R.string.kg_invalid_puk), true); + } + mCheckInProgress = false; + } + }); + } + }.start(); + } + } + + protected CharSequence getSecurityMessageDisplay(int resId) { + // Returns the String in the format + // "SUB:%d : %s", sub, msg + return getContext().getString(R.string.msim_kg_sim_pin_msg_format, + KeyguardUpdateMonitor.getInstance(mContext).getPukLockedSubscription()+1, + getContext().getResources().getText(resId)); + } +} + diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 06f57e2e9fa67..622beecf9ff83 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -2858,6 +2858,8 @@ void TouchInputMapper::configureParameters() { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; } else if (deviceTypeString == "pointer") { mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (deviceTypeString == "gesture") { + mParameters.deviceType = Parameters::DEVICE_TYPE_GESTURE_SENSOR; } else if (deviceTypeString != "default") { ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); } @@ -2906,6 +2908,9 @@ void TouchInputMapper::dumpParameters(String8& dump) { case Parameters::DEVICE_TYPE_POINTER: dump.append(INDENT4 "DeviceType: pointer\n"); break; + case Parameters::DEVICE_TYPE_GESTURE_SENSOR: + dump.append(INDENT4 "DeviceType: gesture\n"); + break; default: ALOG_ASSERT(false); } @@ -2959,6 +2964,9 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; mDeviceMode = DEVICE_MODE_NAVIGATION; + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_GESTURE_SENSOR) { + mSource = AINPUT_SOURCE_GESTURE_SENSOR; + mDeviceMode = DEVICE_MODE_UNSCALED; } else { mSource = AINPUT_SOURCE_TOUCHPAD; mDeviceMode = DEVICE_MODE_UNSCALED; @@ -5709,7 +5717,8 @@ nsecs_t TouchInputMapper::mLastStylusTime = 0; bool TouchInputMapper::rejectPalm(nsecs_t when) { return (when - mLastStylusTime < mConfig.stylusPalmRejectionTime) && - mPointerSimple.currentProperties.toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS; + mPointerSimple.currentProperties.toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS && + mPointerSimple.currentProperties.toolType != AMOTION_EVENT_TOOL_TYPE_ERASER; } bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 7fe7dc1e5bf39..53472677c66d1 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -1219,6 +1219,7 @@ class TouchInputMapper : public InputMapper { DEVICE_TYPE_TOUCH_SCREEN, DEVICE_TYPE_TOUCH_PAD, DEVICE_TYPE_TOUCH_NAVIGATION, + DEVICE_TYPE_GESTURE_SENSOR, DEVICE_TYPE_POINTER, }; diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index fa758a88f4125..b810c65a338f6 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -19,6 +19,7 @@ import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; +import android.app.AppOpsManager; import android.app.IAlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -106,6 +107,8 @@ class AlarmManagerService extends IAlarmManager.Stub { private final PendingIntent mTimeTickSender; private final PendingIntent mDateChangeSender; + private final AppOpsManager mAppOps; + private static final class InFlight extends Intent { final PendingIntent mPendingIntent; final Pair mTarget; @@ -197,6 +200,8 @@ public AlarmManagerService(Context context) { } else { Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); } + + mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); } protected void finalize() throws Throwable { @@ -217,6 +222,24 @@ public void setRepeating(int type, long triggerAtTime, long interval, Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; } + + boolean wakeupFiltered = false; + if ((type == AlarmManager.RTC_WAKEUP + || type == AlarmManager.ELAPSED_REALTIME_WAKEUP) + && mAppOps.checkOpNoThrow(AppOpsManager.OP_ALARM_WAKEUP, + operation.getCreatorUid(), + operation.getCreatorPackage()) + != AppOpsManager.MODE_ALLOWED) { + + if (type == AlarmManager.RTC_WAKEUP) { + type = AlarmManager.RTC; + } else { + type = AlarmManager.ELAPSED_REALTIME; + } + + wakeupFiltered = true; + } + synchronized (mLock) { Alarm alarm = new Alarm(); alarm.type = type; @@ -225,7 +248,16 @@ public void setRepeating(int type, long triggerAtTime, long interval, alarm.operation = operation; // Remove this alarm if already scheduled. - removeLocked(operation); + removeNoWakeupLocked(operation); + final boolean foundExistingWakeup = removeWakeupLocked(operation); + + // note AppOp for accounting purposes + // skip if the alarm already existed + if (!foundExistingWakeup && wakeupFiltered) { + mAppOps.noteOpNoThrow(AppOpsManager.OP_ALARM_WAKEUP, + operation.getCreatorUid(), + operation.getCreatorPackage()); + } if (localLOGV) Slog.v(TAG, "set: " + alarm); @@ -344,21 +376,37 @@ public void removeLocked(PendingIntent operation) { removeLocked(mElapsedRealtimeAlarms, operation); } - private void removeLocked(ArrayList alarmList, + private boolean removeWakeupLocked(PendingIntent operation) { + final boolean rtcwake = removeLocked(mRtcWakeupAlarms, operation); + final boolean realtimewake = removeLocked(mElapsedRealtimeWakeupAlarms, operation); + + return rtcwake || realtimewake; + } + + private void removeNoWakeupLocked(PendingIntent operation) { + removeLocked(mRtcAlarms, operation); + removeLocked(mElapsedRealtimeAlarms, operation); + } + + private boolean removeLocked(ArrayList alarmList, PendingIntent operation) { if (alarmList.size() <= 0) { - return; + return false; } // iterator over the list removing any it where the intent match Iterator it = alarmList.iterator(); + boolean found = false; while (it.hasNext()) { Alarm alarm = it.next(); if (alarm.operation.equals(operation)) { + found = true; it.remove(); } } + + return found; } public void removeLocked(String packageName) { @@ -862,6 +910,10 @@ public void run() fs.numWakeup++; ActivityManagerNative.noteWakeupAlarm( alarm.operation); + // AppOps accounting + mAppOps.noteOpNoThrow(AppOpsManager.OP_ALARM_WAKEUP, + alarm.operation.getCreatorUid(), + alarm.operation.getCreatorPackage()); } } catch (PendingIntent.CanceledException e) { if (alarm.repeatInterval > 0) { diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java index 4efda1cc15e6f..50e4b5a788db1 100644 --- a/services/java/com/android/server/AppOpsService.java +++ b/services/java/com/android/server/AppOpsService.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,16 +33,21 @@ import java.util.Map; import android.app.AppOpsManager; +import android.app.Dialog; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Log; @@ -58,9 +66,13 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import com.android.server.PermissionDialogResult.Result; + public class AppOpsService extends IAppOpsService.Stub { static final String TAG = "AppOps"; static final boolean DEBUG = false; + static final String STRICT_PERMISSION_PROPERTY = "persist.sys.strict_op_enable"; + static final String WHITELIST_FILE = "persist.sys.whitelist"; // Write at most every 30 minutes. static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000; @@ -68,6 +80,9 @@ public class AppOpsService extends IAppOpsService.Stub { Context mContext; final AtomicFile mFile; final Handler mHandler; + final boolean mStrictEnable; + + static final int SHOW_PERMISSION_DIALOG = 1; private static final int[] PRIVACY_GUARD_OP_STATES = new int[] { AppOpsManager.OP_COARSE_LOCATION, @@ -93,6 +108,8 @@ public void run() { } }; + final ArrayList mWhitelist = new ArrayList(); + final SparseArray> mUidOps = new SparseArray>(); @@ -109,14 +126,25 @@ public Ops(String _packageName, int _uid) { public final static class Op { public final int op; public int mode; + public final int defaultMode; public int duration; public long time; public long rejectTime; public int nesting; + public int tempNesting; + public PermissionDialogResult dialogResult; + public int allowedCount; + public int ignoredCount; - public Op(int _op) { + public Op(int _op, boolean strict) { op = _op; - mode = AppOpsManager.MODE_ALLOWED; + if (!strict) { + mode = AppOpsManager.MODE_ALLOWED; + } else { + mode = AppOpsManager.MODE_ASK; + } + dialogResult = new PermissionDialogResult(); + defaultMode = mode; } } @@ -149,11 +177,35 @@ public void binderDied() { } public AppOpsService(File storagePath) { + mStrictEnable = "true".equals(SystemProperties.get(STRICT_PERMISSION_PROPERTY)); mFile = new AtomicFile(storagePath); - mHandler = new Handler(); + mHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) { + case SHOW_PERMISSION_DIALOG: { + Bundle data = msg.getData(); + synchronized (this) { + Op op = (Op) msg.obj; + Result res = (Result) data.getParcelable("result"); + op.dialogResult.register(res); + if (op.dialogResult.mDialog == null) { + Integer code = data.getInt("code"); + Integer uid = data.getInt("uid"); + String packageName = data.getString("packageName"); + PermissionDialog d = new PermissionDialog(mContext, AppOpsService.this, + code, uid, packageName); + op.dialogResult.mDialog = d; + d.show(); + } + } + }break; + } + } + }; + readWhitelist(); readState(); } - + public void publish(Context context) { mContext = context; ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder()); @@ -167,12 +219,14 @@ public void systemReady() { Iterator it = pkgs.values().iterator(); while (it.hasNext()) { Ops ops = it.next(); - int curUid; + int curUid = -1; try { curUid = mContext.getPackageManager().getPackageUid(ops.packageName, UserHandle.getUserId(ops.uid)); } catch (NameNotFoundException e) { - curUid = -1; + if ("android".equals(ops.packageName)) { + curUid = Process.SYSTEM_UID; + } } if (curUid != ops.uid) { Slog.i(TAG, "Pruning old package " + ops.packageName @@ -235,7 +289,8 @@ private ArrayList collectOps(Ops pkgOps, int[] ops) { for (int j=0; j collectOps(Ops pkgOps, int[] ops) { resOps = new ArrayList(); } resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time, - curOp.rejectTime, curOp.duration)); + curOp.rejectTime, curOp.duration, + curOp.allowedCount, curOp.ignoredCount)); } } } @@ -341,11 +397,13 @@ public void setMode(int code, int uid, String packageName, int mode) { } repCbs.addAll(cbs); } - if (mode == AppOpsManager.MODE_ALLOWED) { + + if (mode == op.defaultMode) { // If going into the default mode, prune this op // if there is nothing else interesting in it. pruneOp(op, uid, packageName); } + scheduleWriteNowLocked(); } } @@ -388,23 +446,33 @@ public void resetAllModes() { HashMap>> callbacks = null; synchronized (this) { boolean changed = false; - for (int i=0; i=0; i--) { HashMap packages = mUidOps.valueAt(i); - for (Map.Entry ent : packages.entrySet()) { + Iterator> it = packages.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry ent = it.next(); String packageName = ent.getKey(); Ops pkgOps = ent.getValue(); - for (int j=0; j=0; j--) { Op curOp = pkgOps.valueAt(j); - if (curOp.mode != AppOpsManager.MODE_ALLOWED) { - curOp.mode = AppOpsManager.MODE_ALLOWED; + if (curOp.mode != curOp.defaultMode) { + curOp.mode = curOp.defaultMode; changed = true; callbacks = addCallbacks(callbacks, packageName, curOp.op, mOpModeWatchers.get(curOp.op)); callbacks = addCallbacks(callbacks, packageName, curOp.op, mPackageModeWatchers.get(packageName)); - pruneOp(curOp, mUidOps.keyAt(i), packageName); + if (curOp.time == 0 && curOp.rejectTime == 0) { + pkgOps.removeAt(j); + } } } + if (pkgOps.size() == 0) { + it.remove(); + } + } + if (packages.size() == 0) { + mUidOps.removeAt(i); } } if (changed) { @@ -481,14 +549,136 @@ public void stopWatchingMode(IAppOpsCallback callback) { } } + private boolean isStrict(int code, int uid, String packageName) { + if (!mStrictEnable) { + return false; + } + return ((uid > Process.FIRST_APPLICATION_UID) && + (AppOpsManager.opStrict(code)) && !isInWhitelist(packageName)); + } + + private boolean isInWhitelist(String packageName) { + return mWhitelist.contains(packageName); + } + + private void recordOperationLocked(int code, int uid, String packageName, int mode) { + int switchCode = AppOpsManager.opToSwitch(code); + Op op = getOpLocked(switchCode, uid, packageName, false); + if (op != null) { + if (mode == AppOpsManager.MODE_IGNORED) { + if (DEBUG) Log.d(TAG, "recordOperation: reject #" + mode + " for code " + + switchCode + " (" + code + ") uid " + uid + " package " + packageName); + op.rejectTime = System.currentTimeMillis(); + op.ignoredCount++; + } else if (mode == AppOpsManager.MODE_ALLOWED) { + if (DEBUG) Log.d(TAG, "recordOperation: allowing code " + code + " uid " + uid + + " package " + packageName); + op.time = System.currentTimeMillis(); + op.rejectTime = 0; + op.allowedCount++; + } + } + } + + public void notifyOperation(int code, int uid, String packageName, int mode, + boolean remember) { + verifyIncomingUid(uid); + verifyIncomingOp(code); + ArrayList repCbs = null; + code = AppOpsManager.opToSwitch(code); + synchronized (this) { + Op op = getOpLocked(code, uid, packageName, true); + if (op != null) { + // Record this mode + recordOperationLocked(code, uid, packageName, mode); + + // Increase nesting for all pending startOperation requests + if(mode == AppOpsManager.MODE_ALLOWED) { + if (op.nesting == 0) { + op.time = System.currentTimeMillis(); + op.rejectTime = 0; + op.duration = -1; + } + op.nesting = op.nesting + op.tempNesting; + } + // Reset tempNesting + op.tempNesting = 0; + + // Send result to all waiting client + op.dialogResult.notifyAll(mode); + op.dialogResult.mDialog = null; + + // if User selected to remember her choice + if (remember) { + if (op.mode != mode) { + op.mode = mode; + ArrayList cbs = mOpModeWatchers.get(code); + if (cbs != null) { + if (repCbs == null) { + repCbs = new ArrayList(); + } + repCbs.addAll(cbs); + } + cbs = mPackageModeWatchers.get(packageName); + if (cbs != null) { + if (repCbs == null) { + repCbs = new ArrayList(); + } + repCbs.addAll(cbs); + } + + if (mode == op.defaultMode) { + // If going into the default mode, prune this op + // if there is nothing else interesting in it. + pruneOp(op, uid, packageName); + } + + scheduleWriteNowLocked(); + } + } + } + } + if (repCbs != null) { + for (int i=0; i outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("pkg")) { + String pkgName = parser.getAttributeValue(null, "name"); + mWhitelist.add(pkgName); + } else { + Slog.w(TAG, "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + success = true; + } catch (IllegalStateException e) { + Slog.w(TAG, "Failed parsing " + e); + } catch (NullPointerException e) { + Slog.w(TAG, "Failed parsing " + e); + } catch (NumberFormatException e) { + Slog.w(TAG, "Failed parsing " + e); + } catch (XmlPullParserException e) { + Slog.w(TAG, "Failed parsing " + e); + } catch (IOException e) { + Slog.w(TAG, "Failed parsing " + e); + } catch (IndexOutOfBoundsException e) { + Slog.w(TAG, "Failed parsing " + e); + } finally { + if (!success) { + mWhitelist.clear(); + } + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + } + void readState() { synchronized (mFile) { synchronized (this) { @@ -788,7 +1071,18 @@ void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, String tagName = parser.getName(); if (tagName.equals("op")) { - Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n"))); + int code = AppOpsManager.OP_NONE; + String codeStr = parser.getAttributeValue(null, "n"); + if (codeStr != null) { + code = Integer.parseInt(codeStr); + } + /* use op name string if it exists */ + String codeNameStr = parser.getAttributeValue(null, "ns"); + if (codeNameStr != null) { + code = AppOpsManager.nameToOp(codeNameStr); + } + boolean strict = isStrict(code, uid, pkgName); + Op op = new Op(code, strict); String mode = parser.getAttributeValue(null, "m"); if (mode != null) { op.mode = Integer.parseInt(mode); @@ -805,6 +1099,14 @@ void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, if (dur != null) { op.duration = Integer.parseInt(dur); } + String allowed = parser.getAttributeValue(null, "ac"); + if (allowed != null) { + op.allowedCount = Integer.parseInt(allowed); + } + String ignored = parser.getAttributeValue(null, "ic"); + if (ignored != null) { + op.ignoredCount = Integer.parseInt(ignored); + } HashMap pkgOps = mUidOps.get(uid); if (pkgOps == null) { pkgOps = new HashMap(); @@ -861,9 +1163,8 @@ void writeState() { AppOpsManager.OpEntry op = ops.get(j); out.startTag(null, "op"); out.attribute(null, "n", Integer.toString(op.getOp())); - if (op.getMode() != AppOpsManager.MODE_ALLOWED) { - out.attribute(null, "m", Integer.toString(op.getMode())); - } + out.attribute(null, "ns", AppOpsManager.opToName(op.getOp())); + out.attribute(null, "m", Integer.toString(op.getMode())); long time = op.getTime(); if (time != 0) { out.attribute(null, "t", Long.toString(time)); @@ -876,6 +1177,14 @@ void writeState() { if (dur != 0) { out.attribute(null, "d", Integer.toString(dur)); } + int allowed = op.getAllowedCount(); + if (allowed != 0) { + out.attribute(null, "ac", Integer.toString(allowed)); + } + int ignored = op.getIgnoredCount(); + if (ignored != 0) { + out.attribute(null, "ic", Integer.toString(ignored)); + } out.endTag(null, "op"); } out.endTag(null, "uid"); @@ -958,4 +1267,26 @@ public void setPrivacyGuardSettingForPackage(int uid, String packageName, boolea ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED); } } + + @Override + public void resetCounters() { + mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + synchronized (this) { + for (int i=0; i packages = mUidOps.valueAt(i); + for (Map.Entry ent : packages.entrySet()) { + String packageName = ent.getKey(); + Ops pkgOps = ent.getValue(); + for (int j=0; jBatteryService monitors the charging status, and charge level of the device @@ -165,7 +166,6 @@ public final class BatteryService extends Binder { private boolean mQuietHoursEnabled = false; private int mQuietHoursStart = 0; private int mQuietHoursEnd = 0; - private boolean mQuietHoursDim = true; public BatteryService(Context context, LightsService lights, IDeviceHandler deviceHandler) { mContext = context; @@ -794,7 +794,7 @@ public void updateLightsLocked() { if (!mLightEnabled) { // No lights if explicitly disabled mBatteryLight.turnOff(); - } else if (inQuietHours() && mQuietHoursDim) { + } else if (QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_DIM)) { if (mLedPulseEnabled && level < mLowBatteryWarningLevel && status != BatteryManager.BATTERY_STATUS_CHARGING) { // The battery is low, the device is not charging and the low battery pulse @@ -907,26 +907,9 @@ public void update() { Settings.System.QUIET_HOURS_START, 0, UserHandle.USER_CURRENT_OR_SELF); mQuietHoursEnd = Settings.System.getIntForUser(resolver, Settings.System.QUIET_HOURS_END, 0, UserHandle.USER_CURRENT_OR_SELF); - mQuietHoursDim = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_DIM, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; updateLedPulse(); } } - private boolean inQuietHours() { - if (mQuietHoursEnabled && (mQuietHoursStart != mQuietHoursEnd)) { - // Get the date in "quiet hours" format. - Calendar calendar = Calendar.getInstance(); - int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE); - if (mQuietHoursEnd < mQuietHoursStart) { - // Starts at night, ends in the morning. - return (minutes > mQuietHoursStart) || (minutes < mQuietHoursEnd); - } else { - return (minutes > mQuietHoursStart) && (minutes < mQuietHoursEnd); - } - } - return false; - } - } diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java index 446aab921f65b..1763a1810a34c 100644 --- a/services/java/com/android/server/BluetoothManagerService.java +++ b/services/java/com/android/server/BluetoothManagerService.java @@ -32,6 +32,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.app.AppOpsManager; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -56,6 +57,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final String SYSTEMUI_PACKAGE_NAME="com.android.systemui"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save //Maximum msec to wait for service restart @@ -392,7 +394,7 @@ public boolean enableNoAutoConnect() return true; } - public boolean enable() { + public boolean enable(String callingPackage) { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { Log.w(TAG,"enable(): not allowed for non-active and non system user"); @@ -406,6 +408,13 @@ public boolean enable() { " mBinding = " + mBinding); } + AppOpsManager appOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); + int callingUid = Binder.getCallingUid(); + if (appOps.noteOp(AppOpsManager.OP_BLUETOOTH_CHANGE, callingUid, callingPackage) != + AppOpsManager.MODE_ALLOWED) { + return false; + } + synchronized(mReceiver) { mQuietEnableExternal = false; mEnableExternal = true; @@ -1010,7 +1019,7 @@ public void handleMessage(Message msg) { mState = BluetoothAdapter.STATE_OFF; // enable handleEnable(mQuietEnable); - } else if (mBinding || mBluetooth != null) { + } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; // if user is switched when service is being binding @@ -1019,7 +1028,7 @@ public void handleMessage(Message msg) { if (DBG) { Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2); } - } + } break; } } @@ -1102,11 +1111,20 @@ private boolean checkIfCallerIsForegroundUser() { int callingUid = Binder.getCallingUid(); long callingIdentity = Binder.clearCallingIdentity(); int callingAppId = UserHandle.getAppId(callingUid); + String[] packages = mContext.getPackageManager().getPackagesForUid(callingUid); + boolean isSystemUi = false; + for (String p : packages) { + if (SYSTEMUI_PACKAGE_NAME.equals(p)) { + isSystemUi = true; + break; + } + } boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); - valid = (callingUser == foregroundUser) || - callingAppId == Process.NFC_UID; + valid = (callingUser == foregroundUser) + || callingAppId == Process.NFC_UID + || isSystemUi; if (DBG) { Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 9d1b51402e0c9..41506f139cc42 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -31,6 +31,7 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -144,6 +145,7 @@ import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -1715,10 +1717,17 @@ public void onRestrictBackgroundChanged(boolean restrictBackground) { /** * @see ConnectivityManager#setMobileDataEnabled(boolean) */ - public void setMobileDataEnabled(boolean enabled) { + public void setMobileDataEnabled(String callingPackage, boolean enabled) { enforceChangePermission(); if (DBG) log("setMobileDataEnabled(" + enabled + ")"); + AppOpsManager appOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); + int callingUid = Binder.getCallingUid(); + if (appOps.noteOp(AppOpsManager.OP_DATA_CONNECT_CHANGE, callingUid, callingPackage) != + AppOpsManager.MODE_ALLOWED) { + return; + } + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA, (enabled ? ENABLED : DISABLED), 0)); } @@ -2819,8 +2828,11 @@ public void handleMessage(Message msg) { if (ConnectivityManager.isNetworkTypeMobile(info.getType()) && (0 != Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0)) - && ((state == NetworkInfo.State.CONNECTED) - || info.isConnectedToProvisioningNetwork())) { + && (((state == NetworkInfo.State.CONNECTED) + && (info.getType() == ConnectivityManager.TYPE_MOBILE)) + || info.isConnectedToProvisioningNetwork())) { + log("ConnectivityChange checkMobileProvisioning for" + + " TYPE_MOBILE or ProvisioningNetwork"); checkMobileProvisioning(CheckMp.MAX_TIMEOUT_MS); } @@ -3675,39 +3687,41 @@ private boolean isMobileDataStateTrackerReady() { /** * No connection was possible to the network. + * This is NOT a warm sim. */ - public static final int CMP_RESULT_CODE_NO_CONNECTION = 0; + private static final int CMP_RESULT_CODE_NO_CONNECTION = 0; /** * A connection was made to the internet, all is well. + * This is NOT a warm sim. */ - public static final int CMP_RESULT_CODE_CONNECTABLE = 1; + private static final int CMP_RESULT_CODE_CONNECTABLE = 1; /** - * A connection was made but there was a redirection, we appear to be in walled garden. - * This is an indication of a warm sim on a mobile network. + * A connection was made but no dns server was available to resolve a name to address. + * This is NOT a warm sim since provisioning network is supported. */ - public static final int CMP_RESULT_CODE_REDIRECTED = 2; + private static final int CMP_RESULT_CODE_NO_DNS = 2; /** - * A connection was made but no dns server was available to resolve a name to address. - * This is an indication of a warm sim on a mobile network. + * A connection was made but could not open a TCP connection. + * This is NOT a warm sim since provisioning network is supported. */ - public static final int CMP_RESULT_CODE_NO_DNS = 3; + private static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 3; /** - * A connection was made but could not open a TCP connection. - * This is an indication of a warm sim on a mobile network. + * A connection was made but there was a redirection, we appear to be in walled garden. + * This is an indication of a warm sim on a mobile network such as T-Mobile. */ - public static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 4; + private static final int CMP_RESULT_CODE_REDIRECTED = 4; /** * The mobile network is a provisioning network. - * This is an indication of a warm sim on a mobile network. + * This is an indication of a warm sim on a mobile network such as AT&T. */ - public static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5; + private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5; - AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false); + private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false); @Override public int checkMobileProvisioning(int suggestedTimeOutMs) { @@ -3736,42 +3750,8 @@ public int checkMobileProvisioning(int suggestedTimeOutMs) { return timeOutMs; } - // Start off with notification off - setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null); - - // See if we've alreadying determined if we've got a provsioning connection - // if so we don't need to do anything active - MobileDataStateTracker mdstDefault = (MobileDataStateTracker) - mNetTrackers[ConnectivityManager.TYPE_MOBILE]; - boolean isDefaultProvisioning = mdstDefault.isProvisioningNetwork(); - - MobileDataStateTracker mdstHipri = (MobileDataStateTracker) - mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI]; - boolean isHipriProvisioning = mdstHipri.isProvisioningNetwork(); - - if (isDefaultProvisioning || isHipriProvisioning) { - if (mIsNotificationVisible) { - if (DBG) { - log("checkMobileProvisioning: provisioning-ignore notification is visible"); - } - } else { - NetworkInfo ni = null; - if (isDefaultProvisioning) { - ni = mdstDefault.getNetworkInfo(); - } - if (isHipriProvisioning) { - ni = mdstHipri.getNetworkInfo(); - } - String url = getMobileProvisioningUrl(); - if ((ni != null) && (!TextUtils.isEmpty(url))) { - setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(), url); - } else { - if (DBG) log("checkMobileProvisioning: provisioning but no url, ignore"); - } - } - mIsCheckingMobileProvisioning.set(false); - return timeOutMs; - } + // Start off with mobile notification off + setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null); CheckMp checkMp = new CheckMp(mContext, this); CheckMp.CallBack cb = new CheckMp.CallBack() { @@ -3782,7 +3762,9 @@ void onComplete(Integer result) { mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo(); switch(result) { case CMP_RESULT_CODE_CONNECTABLE: - case CMP_RESULT_CODE_NO_CONNECTION: { + case CMP_RESULT_CODE_NO_CONNECTION: + case CMP_RESULT_CODE_NO_DNS: + case CMP_RESULT_CODE_NO_TCP_CONNECTION: { if (DBG) log("CheckMp.onComplete: ignore, connected or no connection"); break; } @@ -3794,19 +3776,20 @@ void onComplete(Integer result) { } if (TextUtils.isEmpty(url) == false) { if (DBG) log("CheckMp.onComplete: warm (redirected), url=" + url); - setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(), + setProvNotificationVisible(true, + ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(), url); } else { if (DBG) log("CheckMp.onComplete: warm (redirected), no url"); } break; } - case CMP_RESULT_CODE_NO_DNS: - case CMP_RESULT_CODE_NO_TCP_CONNECTION: { + case CMP_RESULT_CODE_PROVISIONING_NETWORK: { String url = getMobileProvisioningUrl(); if (TextUtils.isEmpty(url) == false) { if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), url=" + url); - setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(), + setProvNotificationVisible(true, + ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(), url); } else { if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url"); @@ -3905,8 +3888,26 @@ private synchronized Integer isMobileOk(Params params) { mParams = params; if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { - log("isMobileOk: not mobile capable"); result = CMP_RESULT_CODE_NO_CONNECTION; + log("isMobileOk: X not mobile capable result=" + result); + return result; + } + + // See if we've already determined we've got a provisioning connection, + // if so we don't need to do anything active. + MobileDataStateTracker mdstDefault = (MobileDataStateTracker) + mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE]; + boolean isDefaultProvisioning = mdstDefault.isProvisioningNetwork(); + log("isMobileOk: isDefaultProvisioning=" + isDefaultProvisioning); + + MobileDataStateTracker mdstHipri = (MobileDataStateTracker) + mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI]; + boolean isHipriProvisioning = mdstHipri.isProvisioningNetwork(); + log("isMobileOk: isHipriProvisioning=" + isHipriProvisioning); + + if (isDefaultProvisioning || isHipriProvisioning) { + result = CMP_RESULT_CODE_PROVISIONING_NETWORK; + log("isMobileOk: X default || hipri is provisioning result=" + result); return result; } @@ -3968,8 +3969,8 @@ private synchronized Integer isMobileOk(Params params) { MobileDataStateTracker mdst = (MobileDataStateTracker) mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI]; if (mdst.isProvisioningNetwork()) { - if (DBG) log("isMobileOk: isProvisioningNetwork is true, no TCP conn"); - result = CMP_RESULT_CODE_NO_TCP_CONNECTION; + result = CMP_RESULT_CODE_PROVISIONING_NETWORK; + if (DBG) log("isMobileOk: X isProvisioningNetwork result=" + result); return result; } else { if (DBG) log("isMobileOk: isProvisioningNetwork is false, continue"); @@ -3984,8 +3985,8 @@ private synchronized Integer isMobileOk(Params params) { try { addresses = InetAddress.getAllByName(orgUri.getHost()); } catch (UnknownHostException e) { - log("isMobileOk: UnknownHostException"); result = CMP_RESULT_CODE_NO_DNS; + log("isMobileOk: X UnknownHostException result=" + result); return result; } log("isMobileOk: addresses=" + inetAddressesToString(addresses)); @@ -4007,6 +4008,9 @@ private synchronized Integer isMobileOk(Params params) { addrTried ++) { // Choose the address at random but make sure its type is supported + // TODO: This doesn't work 100% of the time, because we may end up + // trying the same invalid address more than once and ignoring one + // of the valid addresses. InetAddress hostAddr = addresses[rand.nextInt(addresses.length)]; if (((hostAddr instanceof Inet4Address) && linkHasIpv4) || ((hostAddr instanceof Inet6Address) && linkHasIpv6)) { @@ -4031,10 +4035,8 @@ private synchronized Integer isMobileOk(Params params) { } // Rewrite the url to have numeric address to use the specific route. - // I also set the "Connection" to "Close" as by default "Keep-Alive" - // is used which is useless in this case. - URL newUrl = new URL(orgUri.getScheme() + "://" - + hostAddr.getHostAddress() + orgUri.getPath()); + URL newUrl = new URL(orgUri.getScheme(), + hostAddr.getHostAddress(), orgUri.getPath()); log("isMobileOk: newUrl=" + newUrl); HttpURLConnection urlConn = null; @@ -4047,27 +4049,42 @@ private synchronized Integer isMobileOk(Params params) { urlConn.setReadTimeout(SOCKET_TIMEOUT_MS); urlConn.setUseCaches(false); urlConn.setAllowUserInteraction(false); + // Set the "Connection" to "Close" as by default "Keep-Alive" + // is used which is useless in this case. urlConn.setRequestProperty("Connection", "close"); int responseCode = urlConn.getResponseCode(); + + // For debug display the headers + Map> headers = urlConn.getHeaderFields(); + log("isMobileOk: headers=" + headers); + + // Close the connection + urlConn.disconnect(); + urlConn = null; + if (responseCode == 204) { + // Return result = CMP_RESULT_CODE_CONNECTABLE; + log("isMobileOk: X expected responseCode=" + responseCode + + " result=" + result); + return result; } else { + // Retry to be sure this was redirected, we've gotten + // occasions where a server returned 200 even though + // the device didn't have a "warm" sim. + log("isMobileOk: not expected responseCode=" + responseCode); result = CMP_RESULT_CODE_REDIRECTED; } - log("isMobileOk: connected responseCode=" + responseCode); - urlConn.disconnect(); - urlConn = null; - return result; } catch (Exception e) { log("isMobileOk: HttpURLConnection Exception e=" + e); + result = CMP_RESULT_CODE_NO_TCP_CONNECTION; if (urlConn != null) { urlConn.disconnect(); urlConn = null; } } } - result = CMP_RESULT_CODE_NO_TCP_CONNECTION; - log("isMobileOk: loops|timed out"); + log("isMobileOk: X loops|timed out result=" + result); return result; } catch (Exception e) { log("isMobileOk: Exception e=" + e); @@ -4191,7 +4208,7 @@ public void onReceive(Context context, Intent intent) { private void handleMobileProvisioningAction(String url) { // Notication mark notification as not visible - setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null); + setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null); // If provisioning network handle as a special case, // otherwise launch browser with the intent directly. @@ -4277,14 +4294,14 @@ private void setProvNotificationVisible(boolean visible, int networkType, String notification.setLatestEventInfo(mContext, title, details, notification.contentIntent); try { - notificationManager.notify(NOTIFICATION_ID, 1, notification); + notificationManager.notify(NOTIFICATION_ID, networkType, notification); } catch (NullPointerException npe) { loge("setNotificaitionVisible: visible notificationManager npe=" + npe); npe.printStackTrace(); } } else { try { - notificationManager.cancel(NOTIFICATION_ID, 1); + notificationManager.cancel(NOTIFICATION_ID, networkType); } catch (NullPointerException npe) { loge("setNotificaitionVisible: cancel notificationManager npe=" + npe); npe.printStackTrace(); diff --git a/services/java/com/android/server/MSimTelephonyRegistry.java b/services/java/com/android/server/MSimTelephonyRegistry.java new file mode 100644 index 0000000000000..ee9ad9b21fe41 --- /dev/null +++ b/services/java/com/android/server/MSimTelephonyRegistry.java @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.LinkCapabilities; +import android.net.LinkProperties; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.telephony.CellInfo; +import android.telephony.CellLocation; +import android.telephony.MSimTelephonyManager; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Slog; + +import java.util.ArrayList; +import java.util.List; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.net.NetworkInterface; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.telephony.ITelephonyRegistryMSim; +import com.android.internal.telephony.IPhoneStateListener; +import com.android.internal.telephony.DefaultPhoneNotifier; +import com.android.internal.telephony.MSimConstants; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.ServiceStateTracker; +import com.android.internal.telephony.TelephonyIntents; +import com.android.server.am.BatteryStatsService; + +/** + * Since phone process can be restarted, this class provides a centralized place + * that applications can register and be called back from. + */ +class MSimTelephonyRegistry extends ITelephonyRegistryMSim.Stub { + private static final String TAG = "MSimTelephonyRegistry"; + private static final boolean DBG = false; + private static final boolean DBG_LOC = false; + + private static class Record { + String pkgForDebug; + + IBinder binder; + + IPhoneStateListener callback; + + int callerUid; + + int events; + + int subscription; + + @Override + public String toString() { + return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid + + " events=" + Integer.toHexString(events) + "}"; + } + } + + private final Context mContext; + + // access should be inside synchronized (mRecords) for these two fields + private final ArrayList mRemoveList = new ArrayList(); + private final ArrayList mRecords = new ArrayList(); + + private final IBatteryStats mBatteryStats; + + private int[] mCallState; + + private String[] mCallIncomingNumber; + + private ServiceState[] mServiceState; + + private SignalStrength[] mSignalStrength; + + private boolean[] mMessageWaiting; + + private boolean[] mCallForwarding; + + private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + + private int mDataConnectionState = TelephonyManager.DATA_UNKNOWN; + + private boolean mDataConnectionPossible = false; + + private String mDataConnectionReason = ""; + + private String mDataConnectionApn = ""; + + private ArrayList mConnectedApns; + + private LinkProperties mDataConnectionLinkProperties; + + private LinkCapabilities mDataConnectionLinkCapabilities; + + private Bundle[] mCellLocation; + + private int mDataConnectionNetworkType; + + private int mOtaspMode = ServiceStateTracker.OTASP_UNKNOWN; + + private ArrayList> mCellInfo = null; + + private int mDefaultSubscription = 0; + + static final int PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | + PhoneStateListener.LISTEN_CALL_STATE | + PhoneStateListener.LISTEN_DATA_ACTIVITY | + PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | + PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR; + + private static final int MSG_USER_SWITCHED = 1; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_USER_SWITCHED: { + Slog.d(TAG, "MSG_USER_SWITCHED userId=" + msg.arg1); + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + for (int sub = 0; sub < numPhones; sub++) { + MSimTelephonyRegistry.this.notifyCellLocation(mCellLocation[sub], sub); + } + break; + } + } + } + }; + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_USER_SWITCHED.equals(action)) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); + } + } + }; + + // we keep a copy of all of the state so we can send it out when folks + // register for it + // + // In these calls we call with the lock held. This is safe becasuse remote + // calls go through a oneway interface and local calls going through a + // handler before they get to app code. + + MSimTelephonyRegistry(Context context) { + CellLocation location = CellLocation.getEmpty(); + + mContext = context; + mBatteryStats = BatteryStatsService.getService(); + mConnectedApns = new ArrayList(); + + // Initialize default subscription to be used for single standby. + mDefaultSubscription = MSimTelephonyManager.getDefault().getDefaultSubscription(); + + int numPhones = MSimTelephonyManager.getDefault().getPhoneCount(); + mCallState = new int[numPhones]; + mCallIncomingNumber = new String[numPhones]; + mServiceState = new ServiceState[numPhones]; + mSignalStrength = new SignalStrength[numPhones]; + mMessageWaiting = new boolean[numPhones]; + mCallForwarding = new boolean[numPhones]; + mCellLocation = new Bundle[numPhones]; + mCellInfo = new ArrayList>(); + for (int i = 0; i < numPhones; i++) { + mCallState[i] = TelephonyManager.CALL_STATE_IDLE; + mCallIncomingNumber[i] = ""; + mServiceState[i] = new ServiceState(); + mSignalStrength[i] = new SignalStrength(); + mMessageWaiting[i] = false; + mCallForwarding[i] = false; + mCellLocation[i] = new Bundle(); + mCellInfo.add(i, null); + } + + // Note that location can be null for non-phone builds like + // like the generic one. + if (location != null) { + for (int i = 0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + location.fillInNotifierBundle(mCellLocation[i]); + } + } + } + + public void systemReady() { + // Watch for interesting updates + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiver(mBroadcastReceiver, filter); + } + + @Override + public void listen(String pkgForDebug, IPhoneStateListener callback, int events, + boolean notifyNow, int subscription) { + int callerUid = UserHandle.getCallingUserId(); + int myUid = UserHandle.myUserId(); + if (DBG) { + Slog.d(TAG, "listen: E pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events) + + " myUid=" + myUid + + " callerUid=" + callerUid); + } + if (events != 0) { + /* Checks permission and throws Security exception */ + checkListenerPermission(events); + + synchronized (mRecords) { + // register + Record r = null; + find_and_add: { + IBinder b = callback.asBinder(); + final int N = mRecords.size(); + for (int i = 0; i < N; i++) { + r = mRecords.get(i); + if (b == r.binder) { + break find_and_add; + } + } + r = new Record(); + r.binder = b; + r.callback = callback; + r.pkgForDebug = pkgForDebug; + r.callerUid = callerUid; + r.subscription = subscription; + mRecords.add(r); + if (DBG) Slog.i(TAG, "listen: add new record=" + r); + } + int send = events & (events ^ r.events); + r.events = events; + if (notifyNow) { + if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { + try { + r.callback.onServiceStateChanged( + new ServiceState(mServiceState[subscription])); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { + try { + int gsmSignalStrength = mSignalStrength[subscription] + .getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { + try { + r.callback.onMessageWaitingIndicatorChanged( + mMessageWaiting[subscription]); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { + try { + r.callback.onCallForwardingIndicatorChanged( + mCallForwarding[subscription]); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { + try { + if (DBG_LOC) Slog.d(TAG, "listen: mCellLocation=" + + mCellLocation[subscription]); + r.callback.onCellLocationChanged( + new Bundle(mCellLocation[subscription])); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { + try { + r.callback.onCallStateChanged(mCallState[subscription], + mCallIncomingNumber[subscription]); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + try { + r.callback.onDataConnectionStateChanged(mDataConnectionState, + mDataConnectionNetworkType); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { + try { + r.callback.onDataActivity(mDataActivity); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + try { + r.callback.onSignalStrengthsChanged(mSignalStrength[subscription]); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { + try { + r.callback.onOtaspChanged(mOtaspMode); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { + try { + if (DBG_LOC) Slog.d(TAG, "listen: mCellInfo[" + subscription + "]=" + + mCellInfo.get(subscription)); + r.callback.onCellInfoChanged(mCellInfo.get(subscription)); + } catch (RemoteException ex) { + remove(r.binder); + } + } + } + } + } else { + remove(callback.asBinder()); + } + } + + private void remove(IBinder binder) { + synchronized (mRecords) { + final int recordCount = mRecords.size(); + for (int i = 0; i < recordCount; i++) { + if (mRecords.get(i).binder == binder) { + mRecords.remove(i); + return; + } + } + } + } + + public void notifyCallState(int state, String incomingNumber, int subscription) { + if (!checkNotifyPermission("notifyCallState()")) { + return; + } + synchronized (mRecords) { + mCallState[subscription] = state; + mCallIncomingNumber[subscription] = incomingNumber; + for (Record r : mRecords) { + if (((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) && + (r.subscription == subscription)) { + try { + r.callback.onCallStateChanged(state, incomingNumber); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + broadcastCallStateChanged(state, incomingNumber, subscription); + } + + public void notifyServiceState(ServiceState state, int subscription) { + if (!checkNotifyPermission("notifyServiceState()")){ + return; + } + Slog.i(TAG, "notifyServiceState: " + state); + synchronized (mRecords) { + mServiceState[subscription] = state; + for (Record r : mRecords) { + if (((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) && + (r.subscription == subscription)) { + try { + r.callback.onServiceStateChanged(new ServiceState(state)); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + broadcastServiceStateChanged(state, subscription); + } + + public void notifySignalStrength(SignalStrength signalStrength, int subscription) { + if (!checkNotifyPermission("notifySignalStrength()")) { + return; + } + synchronized (mRecords) { + mSignalStrength[subscription] = signalStrength; + for (Record r : mRecords) { + if (((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) && + (r.subscription == subscription)){ + try { + r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength)); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + if (((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) && + (r.subscription == subscription)) { + try { + int gsmSignalStrength = signalStrength.getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + broadcastSignalStrengthChanged(signalStrength, subscription); + } + + public void notifyCellInfo(List cellInfo, int subscription) { + if (!checkNotifyPermission("notifyCellInfo()")) { + return; + } + + synchronized (mRecords) { + mCellInfo.set(subscription, cellInfo); + for (Record r : mRecords) { + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) + && r.subscription == subscription) { + try { + if (DBG_LOC) { + Slog.d(TAG, "notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r); + } + r.callback.onCellInfoChanged(cellInfo); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + + public void notifyMessageWaitingChanged(boolean mwi, int subscription) { + if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { + return; + } + synchronized (mRecords) { + mMessageWaiting[subscription] = mwi; + for (Record r : mRecords) { + if (((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) && + (r.subscription == subscription)) { + try { + r.callback.onMessageWaitingIndicatorChanged(mwi); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + + public void notifyCallForwardingChanged(boolean cfi, int subscription) { + if (!checkNotifyPermission("notifyCallForwardingChanged()")) { + return; + } + synchronized (mRecords) { + mCallForwarding[subscription] = cfi; + for (Record r : mRecords) { + if (((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) && + (r.subscription == subscription)) { + try { + r.callback.onCallForwardingIndicatorChanged(cfi); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + + public void notifyDataActivity(int state) { + if (!checkNotifyPermission("notifyDataActivity()" )) { + return; + } + synchronized (mRecords) { + mDataActivity = state; + for (Record r : mRecords) { + if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { + try { + r.callback.onDataActivity(state); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + + public void notifyDataConnection(int state, boolean isDataConnectivityPossible, + String reason, String apn, String apnType, LinkProperties linkProperties, + LinkCapabilities linkCapabilities, int networkType, boolean roaming) { + if (!checkNotifyPermission("notifyDataConnection()" )) { + return; + } + if (DBG) { + Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible=" + + isDataConnectivityPossible + " reason='" + reason + + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType + + " mRecords.size()=" + mRecords.size() + " mRecords=" + mRecords); + } + synchronized (mRecords) { + boolean modified = false; + if (state == TelephonyManager.DATA_CONNECTED) { + if (!mConnectedApns.contains(apnType)) { + mConnectedApns.add(apnType); + if (mDataConnectionState != state) { + mDataConnectionState = state; + modified = true; + } + } + } else { + if (mConnectedApns.remove(apnType)) { + if (mConnectedApns.isEmpty()) { + mDataConnectionState = state; + modified = true; + } else { + // leave mDataConnectionState as is and + // send out the new status for the APN in question. + } + } + } + mDataConnectionPossible = isDataConnectivityPossible; + mDataConnectionReason = reason; + mDataConnectionLinkProperties = linkProperties; + mDataConnectionLinkCapabilities = linkCapabilities; + if (mDataConnectionNetworkType != networkType) { + mDataConnectionNetworkType = networkType; + // need to tell registered listeners about the new network type + modified = true; + } + if (modified) { + if (DBG) { + Slog.d(TAG, "onDataConnectionStateChanged(" + mDataConnectionState + + ", " + mDataConnectionNetworkType + ")"); + } + for (Record r : mRecords) { + if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + try { + r.callback.onDataConnectionStateChanged(mDataConnectionState, + mDataConnectionNetworkType); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn, + apnType, linkProperties, linkCapabilities, roaming); + } + + public void notifyDataConnectionFailed(String reason, String apnType) { + if (!checkNotifyPermission("notifyDataConnectionFailed()")) { + return; + } + /* + * This is commented out because there is no onDataConnectionFailed callback + * in PhoneStateListener. There should be. + synchronized (mRecords) { + mDataConnectionFailedReason = reason; + final int N = mRecords.size(); + for (int i=N-1; i>=0; i--) { + Record r = mRecords.get(i); + if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_FAILED) != 0) { + // XXX + } + } + } + */ + broadcastDataConnectionFailed(reason, apnType); + } + + public void notifyCellLocation(Bundle cellLocation, int subscription) { + if (!checkNotifyPermission("notifyCellLocation()")) { + return; + } + synchronized (mRecords) { + mCellLocation[subscription] = cellLocation; + for (Record r : mRecords) { + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) + && r.subscription == subscription) { + try { + if (DBG_LOC) { + Slog.d(TAG, "notifyCellLocation: mCellLocation=" + mCellLocation + + " r=" + r); + } + r.callback.onCellLocationChanged(new Bundle(cellLocation)); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + + } + } + handleRemoveListLocked(); + } + } + + public void notifyOtaspChanged(int otaspMode) { + if (!checkNotifyPermission("notifyOtaspChanged()" )) { + return; + } + synchronized (mRecords) { + mOtaspMode = otaspMode; + for (Record r : mRecords) { + if ((r.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { + try { + r.callback.onOtaspChanged(otaspMode); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump telephony.registry from from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + synchronized (mRecords) { + final int recordCount = mRecords.size(); + pw.println("last known state:"); + for (int i = 0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + pw.println(" mCallState[" + i + "]=" + mCallState[i]); + pw.println(" mCallIncomingNumber[" + i + "]=" + mCallIncomingNumber[i]); + pw.println(" mServiceState[" + i + "]=" + mServiceState[i]); + pw.println(" mSignalStrength[" + i + "]=" + mSignalStrength[i]); + pw.println(" mMessageWaiting[" + i + "]=" + mMessageWaiting[i]); + pw.println(" mCallForwarding[" + i + "]=" + mCallForwarding[i]); + pw.println(" mCellLocation[" + i + "]=" + mCellLocation[i]); + pw.println(" mCellInfo[" + i + "]=" + mCellInfo.get(i)); + } + pw.println(" mDataActivity=" + mDataActivity); + pw.println(" mDataConnectionState=" + mDataConnectionState); + pw.println(" mDataConnectionPossible=" + mDataConnectionPossible); + pw.println(" mDataConnectionReason=" + mDataConnectionReason); + pw.println(" mDataConnectionApn=" + mDataConnectionApn); + pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties); + pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities); + pw.println(" mDataConnectionNetworkType=" + mDataConnectionNetworkType); + pw.println("registrations: count=" + recordCount); + for (Record r : mRecords) { + pw.println(" " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events)); + } + } + } + + // + // the legacy intent broadcasting + // + + private void broadcastServiceStateChanged(ServiceState state, int subscription) { + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.notePhoneState(state.getState()); + } catch (RemoteException re) { + // Can't do much + } finally { + Binder.restoreCallingIdentity(ident); + } + + Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); + Bundle data = new Bundle(); + state.fillInNotifierBundle(data); + intent.putExtras(data); + // Pass the subscription along with the intent. + intent.putExtra(MSimConstants.SUBSCRIPTION_KEY, subscription); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int subscription) { + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.notePhoneSignalStrength(signalStrength); + } catch (RemoteException e) { + /* The remote entity disappeared, we can safely ignore the exception. */ + } finally { + Binder.restoreCallingIdentity(ident); + } + + Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + Bundle data = new Bundle(); + signalStrength.fillInNotifierBundle(data); + intent.putExtras(data); + intent.putExtra(MSimConstants.SUBSCRIPTION_KEY, subscription); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private void broadcastCallStateChanged(int state, String incomingNumber, int subscription) { + long ident = Binder.clearCallingIdentity(); + try { + if (state == TelephonyManager.CALL_STATE_IDLE) { + mBatteryStats.notePhoneOff(); + } else { + mBatteryStats.notePhoneOn(); + } + } catch (RemoteException e) { + /* The remote entity disappeared, we can safely ignore the exception. */ + } finally { + Binder.restoreCallingIdentity(ident); + } + + Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + intent.putExtra(PhoneConstants.STATE_KEY, + DefaultPhoneNotifier.convertCallState(state).toString()); + if (!TextUtils.isEmpty(incomingNumber)) { + intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); + } + intent.putExtra(MSimConstants.SUBSCRIPTION_KEY, subscription); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, + android.Manifest.permission.READ_PHONE_STATE); + } + + private void broadcastDataConnectionStateChanged(int state, + boolean isDataConnectivityPossible, + String reason, String apn, String apnType, LinkProperties linkProperties, + LinkCapabilities linkCapabilities, boolean roaming) { + // Note: not reporting to the battery stats service here, because the + // status bar takes care of that after taking into account all of the + // required info. + Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); + intent.putExtra(PhoneConstants.STATE_KEY, + DefaultPhoneNotifier.convertDataState(state).toString()); + if (!isDataConnectivityPossible) { + intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true); + } + if (reason != null) { + intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason); + } + if (linkProperties != null) { + intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties); + String iface = linkProperties.getInterfaceName(); + if (iface != null) { + intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface); + } + } + if (linkCapabilities != null) { + intent.putExtra(PhoneConstants.DATA_LINK_CAPABILITIES_KEY, linkCapabilities); + } + if (roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true); + + intent.putExtra(PhoneConstants.DATA_APN_KEY, apn); + intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private void broadcastDataConnectionFailed(String reason, String apnType) { + Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); + intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason); + intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); + intent.putExtra(MSimConstants.SUBSCRIPTION_KEY, + MSimTelephonyManager.getDefault().getPreferredDataSubscription()); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private boolean checkNotifyPermission(String method) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + String msg = "Modify Phone State Permission Denial: " + method + " from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); + if (DBG) Slog.w(TAG, msg); + return false; + } + + private void checkListenerPermission(int events) { + if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_COARSE_LOCATION, null); + + } + + if ((events & PhoneStateListener.LISTEN_CELL_INFO) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_COARSE_LOCATION, null); + + } + + if ((events & PHONE_STATE_PERMISSION_MASK) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PHONE_STATE, null); + } + } + + private void handleRemoveListLocked() { + if (mRemoveList.size() > 0) { + for (IBinder b: mRemoveList) { + remove(b); + } + mRemoveList.clear(); + } + } + + private boolean validateEventsAndUserLocked(Record r, int events) { + int foregroundUser; + long callingIdentity = Binder.clearCallingIdentity(); + boolean valid = false; + try { + foregroundUser = ActivityManager.getCurrentUser(); + valid = r.callerUid == foregroundUser && (r.events & events) != 0; + if (DBG | DBG_LOC) { + Slog.d(TAG, "validateEventsAndUserLocked: valid=" + valid + + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser + + " r.events=" + r.events + " events=" + events); + } + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + return valid; + } +} diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 9fe2faf9e7833..a5a7cc390baa4 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -1,5 +1,9 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. Apache license notifications and license are + * retained for attribution purposes only. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,9 +56,11 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; +import java.util.List; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.Preconditions; @@ -72,10 +78,12 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -115,6 +123,7 @@ class NetdResponseCode { public static final int TetherDnsFwdTgtListResult = 112; public static final int TtyListResult = 113; + public static final int CommandOkay = 200; public static final int TetherStatusResult = 210; public static final int IpFwdStatusResult = 211; public static final int InterfaceGetCfgResult = 213; @@ -125,6 +134,7 @@ class NetdResponseCode { public static final int TetheringStatsResult = 221; public static final int DnsProxyQueryResult = 222; public static final int ClatdStatusResult = 223; + public static final int V6RtrAdvResult = 224; public static final int InterfaceChange = 600; public static final int BandwidthControl = 601; @@ -485,6 +495,39 @@ public String[] listInterfaces() { } } + @Override + public void addUpstreamV6Interface(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + Slog.d(TAG, "addUpstreamInterface("+ iface + ")"); + try { + final Command cmd = new Command("tether", "interface", "add_upstream"); + cmd.appendArg(iface); + mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Cannot add upstream interface"); + } + } + + @Override + public void removeUpstreamV6Interface(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + Slog.d(TAG, "removeUpstreamInterface(" + iface + ")"); + + try { + final Command cmd = new Command("tether", "interface", "add_upstream"); + cmd.appendArg(iface); + mConnector.execute(cmd); + + mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Cannot remove upstream interface"); + } + } + @Override public InterfaceConfiguration getInterfaceConfig(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -1039,6 +1082,87 @@ public void stopAccessPoint(String wlanIface) { } } + /*Set SAP Channel Range*/ + public void setChannelRange(int startchannel, int endchannel, int band) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + try { + Slog.d(TAG, "Set SAP Channel Range"); + mConnector.execute( + "softap", "qccmd", "set", "setchannelrange=", startchannel, " ", endchannel, " ", band); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating to native daemon to set channel range", e); + } + } + + /*Get SAP Operating Channel*/ + public int getSapOperatingChannel() throws IllegalStateException{ + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + int channel=0; + try { + final NativeDaemonEvent OperChanResp; + Slog.d(TAG, "getSapOperatingChannel"); + OperChanResp = mConnector.execute("softap", "qccmd", "get", "channel"); + Slog.d(TAG, "getSapOperatingChannel--OperChanResp" + OperChanResp); + + //Resp Pattern : 200 8 success channel=6 + final StringTokenizer tok = new StringTokenizer(OperChanResp.getMessage()); + tok.nextToken(); + String temp = (tok.hasMoreTokens()) ? tok.nextToken() : null; + if (temp != null) { + final StringTokenizer tok1 = new StringTokenizer(temp, "="); + String temp1 = (tok1.hasMoreTokens()) ? tok1.nextToken() : null; + String temp2 = (tok1.hasMoreTokens()) ? tok1.nextToken() : null; + if (temp2 != null) + channel = Integer.parseInt(temp2); + } + Slog.d(TAG, "softap qccmd get channel =" + channel); + return channel; + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating to native daemon to getSapOperatingChannel", e); + } + } + + /*Get SAP Auto Channel Selection*/ + public int getSapAutoChannelSelection() throws IllegalStateException{ + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + int autochannel=0; + try { + final NativeDaemonEvent OperChanResp; + Slog.d(TAG, "getSapAutoChannelSelection"); + OperChanResp = mConnector.execute("softap", "qccmd", "get", "autochannel"); + + //Resp Pattern : 200 9 success autochannel=0 + final StringTokenizer tok = new StringTokenizer(OperChanResp.getMessage()); + tok.nextToken(); + String temp = (tok.hasMoreTokens()) ? tok.nextToken() : null; + if (temp != null) { + final StringTokenizer tok1 = new StringTokenizer(temp, "="); + String temp1 = (tok1.hasMoreTokens()) ? tok1.nextToken() : null; + String temp2 = (tok1.hasMoreTokens()) ? tok1.nextToken() : null; + if (temp2 != null) + autochannel = Integer.parseInt(temp2); + } + Slog.d(TAG, "getSapAutoChannelSelection--OperChanResp" + OperChanResp); + Slog.d(TAG, "softap qccmd get autochannel =" + autochannel); + return autochannel; + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating to native daemon to getSapOperatingChannel", e); + } + } + @Override public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -1559,4 +1683,138 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); } + + @Override + public boolean replaceSrcRoute(String iface, byte[] ip, byte[] gateway, int routeId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + final NativeDaemonEvent rsp; + InetAddress ipAddr; + + if (TextUtils.isEmpty(iface)) { + Log.e(TAG,"route cmd failed - iface is invalid"); + return false; + } + + try { + ipAddr = InetAddress.getByAddress(ip); + } catch (UnknownHostException e) { + Log.e(TAG,"route cmd failed because of unknown src ip", e); + return false; + } + + final Command cmd = new Command("route", "replace", "src"); + + if (ipAddr instanceof Inet4Address) + cmd.appendArg("v4"); + else + cmd.appendArg("v6"); + + cmd.appendArg(iface); + cmd.appendArg(ipAddr.getHostAddress()); + cmd.appendArg(routeId); + + try { + InetAddress gatewayAddr = InetAddress.getByAddress(gateway); + // check validity of gw address - add route without gw if its invalid + if ((ipAddr instanceof Inet4Address && gatewayAddr instanceof Inet4Address) || + (ipAddr instanceof Inet6Address && gatewayAddr instanceof Inet6Address)) + { + cmd.appendArg(gatewayAddr.getHostAddress()); + } + } catch (UnknownHostException e) { + Log.w(TAG,"route cmd did not obtain valid gw; adding route without gw"); + } + + try { + rsp = mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + Log.w(TAG,"route cmd failed: ", e); + return false; + } + if (DBG) { + Slog.v(TAG, "replace src route response is " + rsp.toString()); + } + return true; + } + + @Override + public boolean delSrcRoute(byte[] ip, int routeId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + final NativeDaemonEvent rsp; + InetAddress ipAddr; + + try { + ipAddr = InetAddress.getByAddress(ip); + } catch (UnknownHostException e) { + Log.e(TAG,"route cmd failed due to invalid src ip", e); + return false; //cannot remove src route without valid src prefix + } + + final Command cmd = new Command("route", "del", "src"); + + if (ipAddr instanceof Inet4Address) { + cmd.appendArg("v4"); + } else { + cmd.appendArg("v6"); + } + + cmd.appendArg(routeId); + + try { + rsp = mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + Log.w(TAG,"route cmd failed: ", e); + return false; + } + if (DBG) { + Slog.v(TAG, "del src route response is " + rsp.toString()); + } + return true; + } + + @Override + public boolean addRouteWithMetric(String iface, int metric, RouteInfo route) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + final NativeDaemonEvent rsp; + + if (TextUtils.isEmpty(iface)) { + Log.e(TAG,"route cmd failed - iface is invalid"); + return false; + } + + final Command cmd = new Command("route", "add"); + if (route.isDefaultRoute()) { + cmd.appendArg("def"); + } else { + cmd.appendArg("dst"); + } + + InetAddress gateway = route.getGateway(); + if (gateway instanceof Inet4Address) { + cmd.appendArg("v4"); + } else { + cmd.appendArg("v6"); + } + + cmd.appendArg(iface); + cmd.appendArg(metric); + + if (route.isHostRoute()) { + cmd.appendArg(route.getDestination().getAddress().getHostAddress()); + } + + cmd.appendArg(gateway.getHostAddress()); + + try { + rsp = mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + Log.w(TAG,"route cmd failed: ", e); + return false; + } + + if (DBG) { + Slog.v(TAG, "add metric route response is " + rsp.toString()); + } + return true; + } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index d14985e5fe5ab..e98066dbafc41 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -77,6 +77,8 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; +import com.android.internal.util.cm.QuietHoursUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -180,18 +182,6 @@ public class NotificationManagerService extends INotificationManager.Stub private ArrayList mLights = new ArrayList(); private NotificationRecord mLedNotification; - private boolean mQuietHoursEnabled = false; - // Minutes from midnight when quiet hours begin. - private int mQuietHoursStart = 0; - // Minutes from midnight when quiet hours end. - private int mQuietHoursEnd = 0; - // Don't play sounds. - private boolean mQuietHoursMute = true; - // Don't vibrate. - private boolean mQuietHoursStill = true; - // Dim LED if hardware supports it. - private boolean mQuietHoursDim = true; - private final AppOpsManager mAppOps; // contains connections to all connected listeners, including app services @@ -1355,18 +1345,6 @@ void observe() { public void update() { ContentResolver resolver = mContext.getContentResolver(); - mQuietHoursEnabled = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_ENABLED, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; - mQuietHoursStart = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_START, 0, UserHandle.USER_CURRENT_OR_SELF); - mQuietHoursEnd = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_END, 0, UserHandle.USER_CURRENT_OR_SELF); - mQuietHoursMute = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_MUTE, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; - mQuietHoursStill = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_STILL, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; - mQuietHoursDim = Settings.System.getIntForUser(resolver, - Settings.System.QUIET_HOURS_DIM, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; } } @@ -1819,8 +1797,6 @@ public void enqueueNotificationInternal(String pkg, String basePkg, int callingU final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD); synchronized (mNotificationList) { - final boolean inQuietHours = inQuietHours(); - final StatusBarNotification n = new StatusBarNotification( pkg, id, tag, callingUid, callingPid, score, notification, user); NotificationRecord r = new NotificationRecord(n); @@ -1935,14 +1911,16 @@ public void enqueueNotificationInternal(String pkg, String basePkg, int callingU Uri soundUri = null; - if (!(inQuietHours && mQuietHoursMute) && useDefaultSound) { + if (!(QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_MUTE)) + && useDefaultSound) { soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; // check to see if the default notification sound is silent ContentResolver resolver = mContext.getContentResolver(); hasValidSound = Settings.System.getString(resolver, Settings.System.NOTIFICATION_SOUND) != null; - } else if (!(inQuietHours && mQuietHoursMute) && notification.sound != null) { + } else if (!(QuietHoursUtils.inQuietHours(mContext, + Settings.System.QUIET_HOURS_MUTE)) && notification.sound != null) { soundUri = notification.sound; hasValidSound = (soundUri != null); } @@ -1995,7 +1973,7 @@ && shouldConvertSoundToVibration() final boolean useDefaultVibrate = (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; - if (!(inQuietHours && mQuietHoursStill) + if (!(QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_MUTE)) && (useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { mVibrateNotification = r; @@ -2066,21 +2044,6 @@ private boolean canVibrateDuringAlertsDisabled() { 0, UserHandle.USER_CURRENT_OR_SELF) != 0; } - private boolean inQuietHours() { - if (mQuietHoursEnabled && (mQuietHoursStart != mQuietHoursEnd)) { - // Get the date in "quiet hours" format. - Calendar calendar = Calendar.getInstance(); - int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE); - if (mQuietHoursEnd < mQuietHoursStart) { - // Starts at night, ends in the morning. - return (minutes > mQuietHoursStart) || (minutes < mQuietHoursEnd); - } else { - return (minutes > mQuietHoursStart) && (minutes < mQuietHoursEnd); - } - } - return false; - } - private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { AccessibilityManager manager = AccessibilityManager.getInstance(mContext); if (!manager.isEnabled()) { @@ -2354,7 +2317,8 @@ private void updateLightsLocked() { // Don't flash while we are in a call, screen is on or we are // in quiet hours with light dimmed if (mLedNotification == null || mInCall - || (mScreenOn && !mDreaming) || (inQuietHours() && mQuietHoursDim)) { + || (mScreenOn && !mDreaming) + || (QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_DIM))) { mNotificationLight.turnOff(); } else if (mNotificationPulseEnabled) { final Notification ledno = mLedNotification.sbn.getNotification(); diff --git a/services/java/com/android/server/PermissionDialog.java b/services/java/com/android/server/PermissionDialog.java new file mode 100644 index 0000000000000..11b3d0eb7fbcd --- /dev/null +++ b/services/java/com/android/server/PermissionDialog.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR; + +import com.android.internal.R; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +class PermissionDialog extends BasePermissionDialog { + private final static String TAG = "PermissionDialog"; + + private final AppOpsService mService; + private final String mPackageName; + private final int mCode; + private View mView; + private CheckBox mChoice; + private int mUid; + final CharSequence[] mOpLabels; + private Context mContext; + + public static final int ACTION_ALLOWED = 0x2; + public static final int ACTION_IGNORED = 0x4; + public static final int ACTION_IGNORED_TIMEOUT = 0x8; + + // 1-minute timeout, then we automatically dismiss the permission + // dialog + static final long DISMISS_TIMEOUT = 1000 * 60 * 1; + + public PermissionDialog(Context context, AppOpsService service, + int code, int uid, String packageName) { + super(context); + + mContext = context; + Resources res = context.getResources(); + + mService = service; + mCode = code; + mPackageName = packageName; + mUid = uid; + + mOpLabels = res.getTextArray(R.array.app_ops_labels); + + setCancelable(false); + setTitle(R.string.permission_request_notification_title); + getWindow().addFlags(FLAG_SYSTEM_ERROR); + + mView = getLayoutInflater().inflate(R.layout.permission_confirmation_dialog, null); + TextView tv = (TextView) mView.findViewById(R.id.permission_text); + mChoice = (CheckBox) mView.findViewById(R.id.permission_remember_choice_checkbox); + String name = getAppName(mPackageName); + if (name == null) { + name = mPackageName; + } + tv.setText(name + ": " + mOpLabels[mCode]); + setView(mView); + + setButton(DialogInterface.BUTTON_POSITIVE, + res.getString(R.string.allow), mHandler.obtainMessage(ACTION_ALLOWED)); + + setButton(DialogInterface.BUTTON_NEGATIVE, + res.getString(R.string.deny), mHandler.obtainMessage(ACTION_IGNORED)); + + // After the timeout, pretend the user clicked the quit button + //mHandler.sendMessageDelayed( + // mHandler.obtainMessage(ACTION_IGNORED_TIMEOUT), + // DISMISS_TIMEOUT); + } + + private String getAppName(String packageName) { + ApplicationInfo appInfo = null; + PackageManager pm = mContext.getPackageManager(); + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (final NameNotFoundException e) { + return null; + } + if (appInfo != null) { + return (String) pm.getApplicationLabel(appInfo); + } + return null; + } + + private final Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + int mode; + boolean remember = mChoice.isChecked(); + switch(msg.what) { + case ACTION_ALLOWED: + mode = AppOpsManager.MODE_ALLOWED; + break; + case ACTION_IGNORED: + mode = AppOpsManager.MODE_IGNORED; + break; + default: + mode = AppOpsManager.MODE_IGNORED; + remember = false; + break; + } + mService.notifyOperation(mCode, mUid, mPackageName, mode, remember); + dismiss(); + } + }; +} diff --git a/services/java/com/android/server/PermissionDialogResult.java b/services/java/com/android/server/PermissionDialogResult.java new file mode 100644 index 0000000000000..da2c41310f834 --- /dev/null +++ b/services/java/com/android/server/PermissionDialogResult.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Parcel; +import android.os.Parcelable; + +class PermissionDialogResult { + + public final static class Result implements Parcelable { + + boolean mHasResult = false; + int mResult; + + public Result() { + } + + public Result(Parcel in) { + mHasResult = in.readInt() == 1 ? true : false; + mResult = in.readInt(); + } + + public void set(int res) { + synchronized (this) { + mHasResult = true; + mResult = res; + notifyAll(); + } + } + + public int get() { + synchronized (this) { + while (!mHasResult) { + try { + wait(); + } catch (InterruptedException e) { + // Do nothing + } + } + } + return mResult; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public Result createFromParcel(Parcel in) { + return new Result(in); + } + + public Result[] newArray(int size) { + return new Result[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mHasResult ? 1 : 0); + dest.writeInt(mResult); + } + } + + public PermissionDialog mDialog; + public List resultList; + + public PermissionDialogResult() { + mDialog = null; + resultList = new ArrayList(); + } + + public void register(Result res) { + synchronized(this) { + resultList.add(res); + } + } + + public void notifyAll(int mode) { + synchronized(this) { + for (Result res : resultList) { + res.set(mode); + } + resultList.clear(); + } + } +} diff --git a/services/java/com/android/server/RegulatoryObserver.java b/services/java/com/android/server/RegulatoryObserver.java new file mode 100644 index 0000000000000..8413d7673e1a3 --- /dev/null +++ b/services/java/com/android/server/RegulatoryObserver.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.os.UEventObserver; +import android.os.SystemProperties; +import android.provider.Settings; +import android.util.Log; +import android.util.Slog; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + *

RegulatoryObserver monitors for country code change. + */ +class RegulatoryObserver extends UEventObserver { + private static final String TAG = RegulatoryObserver.class.getSimpleName(); + + private static final String REGULATORY_UEVENT_MATCH = "MODALIAS=platform:regulatory"; + private static final String UEVENT_FILE = "/sys/devices/platform/regulatory.0/uevent"; + + private String mCountryKeyword = "COUNTRY="; + private String mCountryCode; + private static final int MSG_COUNTRY_CODE = 0; + + private final Context mContext; + + public RegulatoryObserver(Context context) { + mContext = context; + init(); + startObserving(REGULATORY_UEVENT_MATCH); + } + + @Override + public void onUEvent(UEventObserver.UEvent event) { + Slog.v(TAG, "uevent:\n" + event.toString()); + synchronized (this) { + try { + mCountryCode = event.get("COUNTRY"); + Slog.v(TAG, "Regulatory Country Code:" + mCountryCode); + run_crda(); + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse country code from event " + event); + } + } + } + + private final void init() { + try { + Slog.v(TAG, "RegulatoryObserver init."); + // Read the regulatory uevent file to check if any pending + // uevent before we start observing + BufferedReader uevent_buf = new BufferedReader(new FileReader(UEVENT_FILE)); + String line; + while (((line = uevent_buf.readLine()) != null) + && (line.length() != 0)) { + String[] event_string = line.split("="); + String key = event_string[0]; + String value = event_string[1]; + if (key.equals("COUNTRY")) { + // If it has COUNTRY code, it's a pending request before + // RegulatoryObserver started. Very likely it's from + // CFG80211 which is built in to kernel. + mCountryCode = value; + run_crda(); + } + } + if (uevent_buf != null) { + uevent_buf.close(); + } + } catch (Exception e) { + Slog.e(TAG, "This kernel may not have CRDA support." , e); + } + } + + private final void run_crda() { + try { + if (mCountryCode != null) { + // setprop wlan.crda.country then start wifi-crda service + SystemProperties.set("wlan.crda.country", mCountryCode); + SystemProperties.set("ctl.start", "wifi-crda"); + Slog.v(TAG, "Start wifi-crda service to run crda."); + Slog.v(TAG, "Country Code is " + mCountryCode); + // Broadcast intent is allowed after boot is completed + if ("1".equals(SystemProperties.get("sys.boot_completed"))) { + mHandler.sendEmptyMessage(MSG_COUNTRY_CODE); + Slog.v(TAG, "Send broadcast country code message."); + } + } + } catch (Exception e) { + Slog.e(TAG, "Failed to start wifi-crda service to run crda." , e); + } + } + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_COUNTRY_CODE: + synchronized (this) { + Slog.i(TAG, "Broadcast intent for crda country code: " + mCountryCode); + Intent broadcastIntent = new Intent(); + broadcastIntent.setAction("crda.custom.intent.action.COUNTRY_CODE"); + mContext.sendBroadcast(broadcastIntent); + } + break; + } + } + }; +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 72e8f125ad54a..c7143f2b5e408 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2006 The Android Open Source Project * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. + * Copyright (c) 2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +46,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.service.dreams.DreamService; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -58,6 +62,7 @@ import com.android.server.content.ContentService; import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; +import com.android.server.gesture.GestureService; import com.android.server.input.InputManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; @@ -107,6 +112,32 @@ public void onChange(boolean selfChange) { } } + private class PerformanceProfileObserver extends ContentObserver { + private final String mPropName; + private final String mPropDef; + + public PerformanceProfileObserver(Context ctx) { + super(null); + mPropName = + ctx.getString(com.android.internal.R.string.config_perf_profile_prop); + mPropDef = + ctx.getString(com.android.internal.R.string.config_perf_profile_default_entry); + } + @Override + public void onChange(boolean selfChange) { + setSystemSetting(); + } + + void setSystemSetting() { + String perfProfile = Settings.System.getString(mContentResolver, + Settings.System.PERFORMANCE_PROFILE); + if (perfProfile == null) { + perfProfile = mPropDef; + } + SystemProperties.set(mPropName, perfProfile); + } + } + @Override public void run() { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, @@ -167,6 +198,7 @@ public void run() { BluetoothManagerService bluetooth = null; DockObserver dock = null; RotationSwitchObserver rotateSwitch = null; + RegulatoryObserver regulatory = null; UsbService usb = null; SerialService serial = null; TwilightService twilight = null; @@ -176,6 +208,7 @@ public void run() { CommonTimeManagementService commonTimeMgmtService = null; InputManagerService inputManager = null; TelephonyRegistry telephonyRegistry = null; + MSimTelephonyRegistry msimTelephonyRegistry = null; // Create a shared handler thread for UI within the system server. // This thread is used by at least the following components: @@ -246,6 +279,12 @@ public void run() { telephonyRegistry = new TelephonyRegistry(context); ServiceManager.addService("telephony.registry", telephonyRegistry); + if (android.telephony.MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + Slog.i(TAG, "MSimTelephony Registry"); + msimTelephonyRegistry = new MSimTelephonyRegistry(context); + ServiceManager.addService("telephony.msim.registry", msimTelephonyRegistry); + } + Slog.i(TAG, "Scheduling Policy"); ServiceManager.addService(Context.SCHEDULING_POLICY_SERVICE, new SchedulingPolicyService()); @@ -388,6 +427,7 @@ public void run() { LockSettingsService lockSettings = null; DreamManagerService dreamy = null; PieService pieService = null; + GestureService gestureService = null; // Bring up services needed for UI. if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { @@ -509,6 +549,14 @@ public void run() { reportWtf("starting NetworkPolicy Service", e); } + try { + Slog.i(TAG, "Regulatory Observer"); + // Listen for country code changes + regulatory = new RegulatoryObserver(context); + } catch (Throwable e) { + reportWtf("starting RegulatoryObserver", e); + } + try { Slog.i(TAG, "Wi-Fi P2pService"); wifiP2p = new WifiP2pService(context); @@ -837,6 +885,17 @@ public void run() { } } + if (context.getResources().getBoolean( + com.android.internal.R.bool.config_enableGestureService)) { + try { + Slog.i(TAG, "Gesture Sensor Service"); + gestureService = new GestureService(context, inputManager); + ServiceManager.addService("gesture", gestureService); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting Gesture Sensor Service", e); + } + } + try { Slog.i(TAG, "IdleMaintenanceService"); new IdleMaintenanceService(context, battery); @@ -853,6 +912,16 @@ public void run() { mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ADB_PORT), false, new AdbPortObserver()); + if (!TextUtils.isEmpty(context.getString( + com.android.internal.R.string.config_perf_profile_prop))) { + PerformanceProfileObserver observer = new PerformanceProfileObserver(context); + mContentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.PERFORMANCE_PROFILE), + false, observer); + + // Sync the system property with the current setting + observer.setSystemSetting(); + } // Before things start rolling, be sure we have decided whether // we are in safe mode. @@ -943,6 +1012,14 @@ public void run() { } } + if (gestureService != null) { + try { + gestureService.systemReady(); + } catch (Throwable e) { + reportWtf("making Gesture Sensor Service ready", e); + } + } + IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_APP_LAUNCH_FAILURE); filter.addAction(Intent.ACTION_APP_LAUNCH_FAILURE_RESET); @@ -978,6 +1055,7 @@ public void run() { final DreamManagerService dreamyF = dreamy; final InputManagerService inputManagerF = inputManager; final TelephonyRegistry telephonyRegistryF = telephonyRegistry; + final MSimTelephonyRegistry msimTelephonyRegistryF = msimTelephonyRegistry; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -1115,6 +1193,11 @@ public void run() { } catch (Throwable e) { reportWtf("making TelephonyRegistry ready", e); } + try { + if (msimTelephonyRegistryF != null) msimTelephonyRegistryF.systemReady(); + } catch (Throwable e) { + reportWtf("making TelephonyRegistry ready", e); + } } }); diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java index 08a3333c7dd80..e4a946340c3bc 100644 --- a/services/java/com/android/server/VibratorService.java +++ b/services/java/com/android/server/VibratorService.java @@ -43,9 +43,9 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; +import com.android.internal.util.cm.QuietHoursUtils; import java.util.ArrayList; -import java.util.Calendar; import java.util.LinkedList; import java.util.ListIterator; @@ -179,29 +179,6 @@ public boolean hasVibrator() { return doVibratorExists(); } - private boolean inQuietHours() { - boolean quietHoursEnabled = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.QUIET_HOURS_ENABLED, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; - int quietHoursStart = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.QUIET_HOURS_START, 0, UserHandle.USER_CURRENT_OR_SELF); - int quietHoursEnd = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.QUIET_HOURS_END, 0, UserHandle.USER_CURRENT_OR_SELF); - boolean quietHoursHaptic = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.QUIET_HOURS_HAPTIC, 0, UserHandle.USER_CURRENT_OR_SELF) != 0; - if (quietHoursEnabled && quietHoursHaptic && (quietHoursStart != quietHoursEnd)) { - // Get the date in "quiet hours" format. - Calendar calendar = Calendar.getInstance(); - int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE); - if (quietHoursEnd < quietHoursStart) { - // Starts at night, ends in the morning. - return (minutes > quietHoursStart) || (minutes < quietHoursEnd); - } else { - return (minutes > quietHoursStart) && (minutes < quietHoursEnd); - } - } - return false; - } - private void verifyIncomingUid(int uid) { if (uid == Binder.getCallingUid()) { return; @@ -223,7 +200,8 @@ public void vibrate(int uid, String packageName, long milliseconds, IBinder toke // timeout of 0 or negative. This will ensure that a vibration has // either a timeout of > 0 or a non-null pattern. if (milliseconds <= 0 || (mCurrentVibration != null - && mCurrentVibration.hasLongerTimeout(milliseconds)) || inQuietHours()) { + && mCurrentVibration.hasLongerTimeout(milliseconds)) + || QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_HAPTIC)) { // Ignore this vibration since the current vibration will play for // longer than milliseconds. return; @@ -260,7 +238,7 @@ public void vibratePattern(int uid, String packageName, long[] pattern, int repe != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); } - if (inQuietHours()) { + if (QuietHoursUtils.inQuietHours(mContext, Settings.System.QUIET_HOURS_HAPTIC)) { return; } verifyIncomingUid(uid); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 848e09486b00d..9589375f957ee 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -11815,8 +11815,12 @@ public Intent registerReceiver(IApplicationThread caller, String callerPackage, "Receiver requested to register for user " + userId + " was previously registered for user " + rl.userId); } + + boolean isSystem = callerApp != null ? + (callerApp.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 : false; + BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, - permission, callingUid, userId, (callerApp.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + permission, callingUid, userId, isSystem); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index c524a78e5f4cf..a158d070ce3ff 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1308,6 +1308,10 @@ final void ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord r; boolean behindFullscreen = false; for (; i>=0; i--) { + // To make sure index is valid as there might be the case when size of + // the history stack gets decremented within this for loop. + if (i >= mHistory.size()) + continue; r = mHistory.get(i); if (DEBUG_VISBILITY) Slog.v( TAG, "Make visible? " + r + " finishing=" + r.finishing diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java index a9454bd4a4bef..b4590963dc136 100644 --- a/services/java/com/android/server/am/NativeCrashListener.java +++ b/services/java/com/android/server/am/NativeCrashListener.java @@ -152,6 +152,14 @@ public void run() { Slog.d(TAG, "Exception writing ack: " + e.getMessage()); } } + finally { + try { + Slog.d(TAG, "Closing socket connection: " + peerFd); + Libcore.os.close(peerFd); + } catch (ErrnoException e) { + Slog.w(TAG, "Error closing socket connection", e); + } + } } } } diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 6dae4aa1754cf..dcdf966bdd7bc 100644 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -366,7 +366,7 @@ private void readHistoryStatsFLOCK(AtomicFile file) { XmlPullParser parser = Xml.newPullParser(); parser.setInput(fis, null); int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG) { + while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { eventType = parser.next(); } String tagName = parser.getName(); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 080b3c682e006..825327654f06c 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -545,7 +545,7 @@ public void onReceive(Context content, Intent intent) { if (networkInfo != null && networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) { if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION"); - mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED, networkInfo); } } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { updateConfiguration(); @@ -1318,7 +1318,55 @@ protected boolean turnOffMasterTetherSettings() { return true; } + protected void addUpstreamV6Interface(String iface) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + Log.d(TAG, "adding v6 interface " + iface); + try { + service.addUpstreamV6Interface(iface); + } catch (Exception e) { + Log.e(TAG, "Unable to append v6 upstream interface", e); + } + } + + protected void removeUpstreamV6Interface(String iface) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + Log.d(TAG, "removing v6 interface " + iface); + try { + service.removeUpstreamV6Interface(iface); + } catch (Exception e) { + Log.e(TAG, "Unable to remove v6 upstream interface", e); + } + } + + + boolean isIpv6Connected(IConnectivityManager cm, LinkProperties linkProps) { + boolean ret = false; + Collection addresses = null; + + if (cm == null || linkProps == null) { + return false; + } + addresses = linkProps.getAddresses(); + for (InetAddress addr: addresses) { + if (addr instanceof java.net.Inet6Address) { + java.net.Inet6Address i6addr = (java.net.Inet6Address) addr; + if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() && + !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) { + ret = true; + break; + } + } + } + return ret; + } + protected void chooseUpstreamType(boolean tryCell) { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b); int upType = ConnectivityManager.TYPE_NONE; String iface = null; @@ -1334,11 +1382,20 @@ protected void chooseUpstreamType(boolean tryCell) { for (Integer netType : mUpstreamIfaceTypes) { NetworkInfo info = null; + LinkProperties props = null; + boolean isV6Connected = false; try { - info = mConnService.getNetworkInfo(netType.intValue()); + info = cm.getNetworkInfo(netType.intValue()); + if (info != null) { + props = cm.getLinkProperties(info.getType()); + isV6Connected = isIpv6Connected(cm, props); + } } catch (RemoteException e) { } if ((info != null) && info.isConnected()) { upType = netType.intValue(); + if (isV6Connected) { + addUpstreamV6Interface(props.getInterfaceName()); + } break; } } @@ -1510,8 +1567,21 @@ public boolean processMessage(Message message) { break; case CMD_UPSTREAM_CHANGED: // need to try DUN immediately if Wifi goes down + NetworkInfo info = (NetworkInfo) message.obj; mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; chooseUpstreamType(mTryCell); + if (info != null) { + if (!info.isConnected()) { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b); + try { + LinkProperties props = cm.getLinkProperties(info.getType()); + removeUpstreamV6Interface(props.getInterfaceName()); + } catch(Exception e) { + Log.e(TAG, "Exception querying ConnectivityManager", e); + } + } + } mTryCell = !mTryCell; break; case CMD_CELL_CONNECTION_RENEW: diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 17b066272262a..741ea252787ca 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2012 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +25,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IDisplayManager; import android.hardware.display.IDisplayManagerCallback; +import android.hardware.display.IRemoteDisplayAdapter; import android.hardware.display.WifiDisplayStatus; import android.os.Binder; import android.os.Handler; @@ -170,6 +172,8 @@ public final class DisplayManagerService extends IDisplayManager.Stub { // The Wifi display adapter, or null if not registered. private WifiDisplayAdapter mWifiDisplayAdapter; + private RemoteDisplayAdapter mRemoteDisplayAdapter; + // Viewports of the default display and the display that should receive touch // input from an external source. Used by the input system. private final DisplayViewport mDefaultViewport = new DisplayViewport(); @@ -582,11 +586,30 @@ private void registerDefaultDisplayAdapter() { } } + public void scanRemoteDisplays() { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mRemoteDisplayAdapter != null) { + mRemoteDisplayAdapter.mStub.scanRemoteDisplays(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public IRemoteDisplayAdapter getRemoteDisplayAdapter() { + return mRemoteDisplayAdapter.mStub; + } + private void registerAdditionalDisplayAdapters() { synchronized (mSyncRoot) { if (shouldRegisterNonEssentialDisplayAdaptersLocked()) { registerOverlayDisplayAdapterLocked(); registerWifiDisplayAdapterLocked(); + registerRemoteDisplayAdapterLocked(); } } } @@ -607,6 +630,17 @@ private void registerWifiDisplayAdapterLocked() { } } + private void registerRemoteDisplayAdapterLocked() { + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableWifiDisplay) + || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) { + mRemoteDisplayAdapter = new RemoteDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener, + mPersistentDataStore); + registerDisplayAdapterLocked(mRemoteDisplayAdapter); + } + } + private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() { // In safe mode, we disable non-essential display adapters to give the user // an opportunity to fix broken settings or other problems that might affect diff --git a/services/java/com/android/server/display/RemoteDisplayAdapter.java b/services/java/com/android/server/display/RemoteDisplayAdapter.java new file mode 100644 index 0000000000000..cb997ba93a37c --- /dev/null +++ b/services/java/com/android/server/display/RemoteDisplayAdapter.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.hardware.display.DisplayManager; +import android.hardware.display.IDisplayDevice; +import android.hardware.display.IRemoteDisplayAdapter; +import android.hardware.display.WifiDisplay; +import android.hardware.display.WifiDisplayStatus; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceControl; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +public class RemoteDisplayAdapter extends DisplayAdapter { + private static class RemoteDisplay { + RemoteDisplayDevice remoteDisplayDevice; + DisplayDeviceInfo info; + IDisplayDevice displayDevice; + boolean hidden; + } + + private static final String TAG = "RemoteDisplayAdapter"; + private static final String SCAN = "com.cyanogenmod.server.display.SCAN"; + private static final String STOP_SCAN = "com.cyanogenmod.server.display.STOP_SCAN"; + + private Hashtable mDevices = new Hashtable(); + private boolean mScanning; + private Handler mHandler; + private RemoteDisplay mActiveDisplay; + Stub mStub = new Stub(); + + class Stub extends IRemoteDisplayAdapter.Stub { + public void registerDisplayDevice(IDisplayDevice displayDevice, String name, int width, + int height, float refreshRate, int flags, String address, boolean hidden) { + synchronized (getSyncRoot()) { + RemoteDisplay display = new RemoteDisplay(); + display.hidden = hidden; + display.info = new DisplayDeviceInfo(); + display.displayDevice = displayDevice; + mDevices.put(address, display); + DisplayDeviceInfo info = display.info; + info.name = name; + info.width = width; + info.height = height; + info.refreshRate = refreshRate; + info.flags = flags; + info.type = Display.TYPE_UNKNOWN; + info.address = address; + info.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; + info.setAssumedDensityForExternalDisplay(width, height); + handleSendStatusChangeBroadcast(); + } + } + + @Override + public void unregisterDisplayDevice(IDisplayDevice displayDevice) { + synchronized (getSyncRoot()) { + for (RemoteDisplay display: mDevices.values()) { + if (display.displayDevice == displayDevice) { + mDevices.remove(display.info.address); + if (mActiveDisplay == display) { + disconnectRemoteDisplay(); + } + return; + } + } + } + } + + @Override + public void scanRemoteDisplays() { + synchronized (getSyncRoot()) { + mScanning = true; + Intent scan = new Intent(SCAN); + PackageManager pm = getContext().getPackageManager(); + List services = pm.queryIntentServices(scan, 0); + if (services == null) + return; + for (ResolveInfo info : services) { + Intent intent = new Intent(); + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + intent.setComponent(name); + intent.setAction(SCAN); + getContext().startService(intent); + } + handleSendStatusChangeBroadcast(); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + stopScan(); + } + }, 10000L); + } + } + + @Override + public void connectRemoteDisplay(String address) throws RemoteException { + synchronized (getSyncRoot()) { + RemoteDisplay display = mDevices.get(address); + if (display == null) { + Log.e(TAG, "Could not find display?"); + return; + } + if (display.remoteDisplayDevice != null) { + Log.e(TAG, "Display device is already connected?"); + return; + } + Surface surface; + try { + surface = display.displayDevice.createDisplaySurface(); + if (surface == null) { + Log.e(TAG, "Returned null Surface"); + return; + } + } catch (RemoteException e) { + Log.e(TAG, "Error creating surface", e); + return; + } + IBinder displayToken = SurfaceControl.createDisplay(display.info.name, false); + display.remoteDisplayDevice = new RemoteDisplayDevice(surface, displayToken, + display.info); + mActiveDisplay = display; + sendDisplayDeviceEventLocked(display.remoteDisplayDevice, + DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED); + handleSendStatusChangeBroadcast(); + + new Runnable() { + public void run() { + RemoteDisplay activeDisplay = mActiveDisplay; + if (activeDisplay == null) + return; + if (activeDisplay.displayDevice == null) + return; + if (activeDisplay.displayDevice.asBinder().isBinderAlive()) { + mHandler.postDelayed(this, 5000); + return; + } + synchronized (getSyncRoot()) { + stopActiveDisplayLocked(); + } + } + }.run(); + } + } + + @Override + public void disconnectRemoteDisplay() { + synchronized (getSyncRoot()) { + stopActiveDisplayLocked(); + handleSendStatusChangeBroadcast(); + } + } + + @Override + public void forgetRemoteDisplay(String address) { + synchronized (getSyncRoot()) { + } + } + + @Override + public void renameRemoteDisplay(String address, String alias) { + synchronized (getSyncRoot()) { + } + } + + @Override + public WifiDisplayStatus getRemoteDisplayStatus() { + synchronized (getSyncRoot()) { + ArrayList availableDisplays = new ArrayList(); + ArrayList list = new ArrayList(mDevices.values()); + for (RemoteDisplay display : list) { + if (!display.displayDevice.asBinder().isBinderAlive()) { + mDevices.remove(display.info.address); + if (mActiveDisplay == display) { + stopActiveDisplayLocked(); + } + continue; + } + WifiDisplay add = new WifiDisplay(display.info.address, display.info.name, + display.info.name, display.hidden); + availableDisplays.add(add); + } + + WifiDisplay active = null; + if (mActiveDisplay != null) + active = new WifiDisplay(mActiveDisplay.info.address, mActiveDisplay.info.name, + mActiveDisplay.info.name); + + WifiDisplayStatus status = new WifiDisplayStatus( + WifiDisplayStatus.FEATURE_STATE_ON, + mScanning ? WifiDisplayStatus.SCAN_STATE_SCANNING + : WifiDisplayStatus.SCAN_STATE_NOT_SCANNING, + WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED, active, + availableDisplays.toArray(new WifiDisplay[0]), WifiDisplay.EMPTY_ARRAY); + return status; + } + } + }; + + private void stopActiveDisplayLocked() { + if (mActiveDisplay != null && mActiveDisplay.remoteDisplayDevice != null) { + try { + mActiveDisplay.displayDevice.stop(); + } catch (Exception e) { + } + mActiveDisplay.remoteDisplayDevice.clearSurfaceLocked(); + sendDisplayDeviceEventLocked(mActiveDisplay.remoteDisplayDevice, + DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED); + mActiveDisplay.remoteDisplayDevice = null; + } + mActiveDisplay = null; + } + + public void stopScan() { + mScanning = false; + Intent scan = new Intent(SCAN); + PackageManager pm = getContext().getPackageManager(); + List services = pm.queryIntentServices(scan, 0); + if (services == null) { + return; + } + for (ResolveInfo info : services) { + Intent intent = new Intent(); + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + intent.setComponent(name); + intent.setAction(STOP_SCAN); + getContext().startService(intent); + } + handleSendStatusChangeBroadcast(); + } + + // Runs on the handler. + private void handleSendStatusChangeBroadcast() { + mHandler.post(new Runnable() { + @Override + public void run() { + final Intent intent = new Intent( + DisplayManager.ACTION_REMOTE_DISPLAY_STATUS_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(DisplayManager.EXTRA_REMOTE_DISPLAY_STATUS, + mStub.getRemoteDisplayStatus()); + + // Send protected broadcast about wifi display status to + // registered receivers. + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); + } + }); + } + + private class RemoteDisplayDevice extends DisplayDevice { + private Surface surface; + private DisplayDeviceInfo mInfo; + + public RemoteDisplayDevice(Surface surface, IBinder displayToken, DisplayDeviceInfo info) { + super(RemoteDisplayAdapter.this, displayToken); + this.surface = surface; + this.mInfo = info; + } + + public void clearSurfaceLocked() { + surface = null; + sendTraversalRequestLocked(); + } + + @Override + public void performTraversalInTransactionLocked() { + setSurfaceInTransactionLocked(surface); + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + synchronized (getSyncRoot()) { + return mInfo; + } + } + } + + public RemoteDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, + Handler handler, Listener listener, PersistentDataStore persistentDataStore) { + super(syncRoot, context, handler, listener, TAG); + this.mHandler = new Handler(handler.getLooper()); + } +} diff --git a/services/java/com/android/server/gesture/GestureInputFilter.java b/services/java/com/android/server/gesture/GestureInputFilter.java new file mode 100644 index 0000000000000..e40761fc61f84 --- /dev/null +++ b/services/java/com/android/server/gesture/GestureInputFilter.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.server.gesture; + +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; +import android.content.Context; +import android.hardware.input.InputManager; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Slog; +import android.view.Display; +import android.view.GestureDetector; +import android.view.GestureDetector.OnDoubleTapListener; +import android.view.IInputFilter; +import android.view.IInputFilterHost; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.MotionEvent; +import android.view.OrientationEventListener; +import android.view.ViewConfiguration; +import android.view.WindowManager; +import java.io.PrintWriter; + +/** + * A simple input filter that listens for gesture sensor events and converts + * them to input events to be injected into the input stream. + */ +public class GestureInputFilter implements IInputFilter, GestureDetector.OnGestureListener, OnDoubleTapListener { + + private static final String TAG = "GestureInputFilter"; + private static final boolean DEBUG = false; + + private IInputFilterHost mHost = null; + + private GestureDetector mGestureDetector; + private InputManager mInputManager; + private OrientationEventListener mOrientationListener; + private final int mScreenWidth, mScreenHeight; + private float mGesturePadWidth, mGesturePadHeight; + private int mTouchSlop, mOrientation; + private Context mContext; + private PendingIntent mLongPressPendingIntent; + private PendingIntent mDoubleClickPendingIntent; + + public GestureInputFilter(Context context) { + mInputManager = InputManager.getInstance(); + mContext = context; + for (int id : mInputManager.getInputDeviceIds()) { + InputDevice inputDevice = mInputManager.getInputDevice(id); + if ((inputDevice.getSources() & InputDevice.SOURCE_GESTURE_SENSOR) + == mInputManager.getInputDevice(id).getSources()) { + mGesturePadWidth = inputDevice.getMotionRange(MotionEvent.AXIS_X).getMax(); + mGesturePadHeight = inputDevice.getMotionRange(MotionEvent.AXIS_Y).getMax(); + break; + } + } + ViewConfiguration vc = ViewConfiguration.get(context); + mTouchSlop = vc.getScaledTouchSlop(); + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + mScreenWidth = display.getWidth(); + mScreenHeight = display.getHeight(); + mGestureDetector = new GestureDetector(context, this); + mGestureDetector.setOnDoubleTapListener(this); + mOrientationListener = new OrientationEventListener(context) { + @Override + public void onOrientationChanged(int orientation) { + if (orientation == -1) { + return; + } + mOrientation = (orientation + 45) / 90 * 90; + } + }; + } + + /** + * Called to enqueue the input event for filtering. + * The event must be recycled after the input filter processed it. + * This method is guaranteed to be non-reentrant. + * + * @see InputFilter#filterInputEvent(InputEvent, int) + * @param event The input event to enqueue. + */ + // called by the input dispatcher thread + public void filterInputEvent(InputEvent event, int policyFlags) + throws RemoteException { + if (DEBUG) Slog.d(TAG, event.toString()); + + try { + if (event.getSource() != InputDevice.SOURCE_GESTURE_SENSOR + || !(event instanceof MotionEvent)) { + try { + mHost.sendInputEvent(event, policyFlags); + } catch (RemoteException e) { + /* ignore */ + } + return; + } + + MotionEvent motionEvent = (MotionEvent) event; + mGestureDetector.onTouchEvent(motionEvent); + } finally { + event.recycle(); + } + } + + // called by the input dispatcher thread + public void install(IInputFilterHost host) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "Gesture input filter installed."); + } + mHost = host; + mOrientationListener.enable(); + } + + // called by the input dispatcher thread + public void uninstall() throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "Gesture input filter uninstalled."); + } + mHost = null; + mOrientationListener.disable(); + mContext = null; + } + + // should never be called + public IBinder asBinder() { + throw new UnsupportedOperationException(); + } + + // called by a Binder thread + public void dump(PrintWriter pw, String prefix) { + + } + + private boolean generateSwipe(MotionEvent e1, MotionEvent e2) { + switch (mOrientation) { + case 90: + Slog.d(TAG, "Adjusting motion for 90 degrees"); + e1.setLocation(e1.getY(), e1.getX()); + e2.setLocation(e2.getY(), e2.getX()); + break; + case 180: + Slog.d(TAG, "Adjusting motion for 180 degrees"); + e1.setLocation(mGesturePadWidth - e1.getX(), + mGesturePadHeight - e1.getY()); + e2.setLocation(mGesturePadWidth - e2.getX(), + mGesturePadHeight - e2.getY()); + break; + case 270: + Slog.d(TAG, "Adjusting motion for 270 degrees"); + e1.setLocation(mGesturePadHeight - e1.getY(), + e1.getX()); + e2.setLocation(mGesturePadHeight - e2.getY(), + e2.getX()); + break; + } + + float deltaX = Math.abs(e1.getX() - e2.getX()); + float deltaY = Math.abs(e1.getY() - e2.getY()); + + if (deltaX < mTouchSlop && deltaY < mTouchSlop) { + return false; + } + + if (deltaX > deltaY) { + e2.setLocation(e2.getX(), e1.getY()); + } else if (deltaY > deltaX) { + e2.setLocation(e1.getX(), e2.getY()); + } + + float scaleX = mScreenWidth / mGesturePadWidth; + float scaleY = mScreenHeight / mGesturePadHeight; + + float magnitudeX = deltaX * scaleX; + float magnitudeY = deltaY * scaleY; + + float origX = mScreenWidth / 2; + float origY = mScreenHeight / 2; + float endX = 0.0f; + float endY = 0.0f; + + if (e2.getY() > e1.getY()) { + if (DEBUG) Slog.d(TAG, "Detected down motion"); + // Ensure selection does not occur + endX = origX + mTouchSlop + 5; + endY = origY + magnitudeY; + } else if (e2.getY() < e1.getY()) { + if (DEBUG) Slog.d(TAG, "Detected up motion"); + endX = origX + mTouchSlop + 5; + endY = origY - magnitudeY; + } else if (e2.getX() > e1.getX()) { + if (DEBUG) Slog.d(TAG, "Detected left motion"); + endX = origX + magnitudeX; + endY = origY + mTouchSlop + 5; + } else if (e2.getX() < e1.getX()) { + if (DEBUG) Slog.d(TAG, "Detected right motion"); + endX = origX - magnitudeX; + endY = origY + mTouchSlop + 5; + } else { + return false; + } + + sendSwipe(origX, origY, endX, endY); + return true; + } + + private void sendSwipe(float x1, float y1, float x2, float y2) { + final long duration = 100; + long now = SystemClock.uptimeMillis(); + final long startTime = now; + final long endTime = startTime + duration; + sendMotionEvent(MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); + + while (now < endTime) { + long elapsedTime = now - startTime; + float alpha = (float) elapsedTime / duration; + sendMotionEvent(MotionEvent.ACTION_MOVE, now, + lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f); + now = SystemClock.uptimeMillis(); + } + sendMotionEvent(MotionEvent.ACTION_UP, now, x2, y2, 1.0f); + } + + private void sendMotionEvent(int action, long when, float x, float y, + float pressure) { + final float DEFAULT_SIZE = 1.0f; + final int DEFAULT_META_STATE = 0; + final float DEFAULT_PRECISION_X = 1.0f; + final float DEFAULT_PRECISION_Y = 1.0f; + final int DEFAULT_DEVICE_ID = 0; + final int DEFAULT_EDGE_FLAGS = 0; + + MotionEvent e = MotionEvent.obtain(when, when, action, x, y, pressure, + DEFAULT_SIZE, DEFAULT_META_STATE, DEFAULT_PRECISION_X, + DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID, DEFAULT_EDGE_FLAGS); + e.setSource(InputDevice.SOURCE_TOUCHSCREEN); + sendInputEvent(e); + } + + private void sendInputEvent(InputEvent event) { + mInputManager.injectInputEvent(event, + InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); + } + + private static final float lerp(float a, float b, float alpha) { + return (b - a) * alpha + a; + } + + @Override + public boolean onDown(MotionEvent e) { + return false; + } + + @Override + public void onShowPress(MotionEvent e) { + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + return false; + } + + @Override + public void onLongPress(MotionEvent e) { + if (mLongPressPendingIntent != null) { + try { + mLongPressPendingIntent.send(); + } catch (CanceledException e1) { + e1.printStackTrace(); + } + } + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + return generateSwipe(e1, e2); + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + if (mDoubleClickPendingIntent != null) { + try { + mDoubleClickPendingIntent.send(); + return true; + } catch (CanceledException e1) { + e1.printStackTrace(); + } + } + + return false; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + return false; + } + + public void setOnLongPressPendingIntent(PendingIntent pendingIntent) { + mLongPressPendingIntent = pendingIntent; + } + + public void setOnDoubleClickPendingIntent(PendingIntent pendingIntent) { + mDoubleClickPendingIntent = pendingIntent; + } +} diff --git a/services/java/com/android/server/gesture/GestureService.java b/services/java/com/android/server/gesture/GestureService.java new file mode 100644 index 0000000000000..1a01e415ebec8 --- /dev/null +++ b/services/java/com/android/server/gesture/GestureService.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project (Jens Doll) + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.server.gesture; + +import android.Manifest; +import android.app.PendingIntent; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.service.gesture.IGestureService; +import android.util.Slog; + +import com.android.server.input.InputManagerService; + +/** + * A system service to track gesture sensor gestures. This service is + * responsible for creating input events from motion events generated by + * gesture sensor input hardware: + *

  • Installing an input filter to listen for gesture sensor events
  • + *
  • Generating input events to be injected into the input stream
  • + */ +public class GestureService extends IGestureService.Stub { + public static final String TAG = "GestureService"; + public static final boolean DEBUG = false; + + private Context mContext; + private InputManagerService mInputManager; + private GestureInputFilter mInputFilter; + + public GestureService(Context context, InputManagerService inputManager) { + mContext = context; + mInputManager = inputManager; + } + + // called by system server + public void systemReady() { + if (DEBUG) Slog.d(TAG, "Starting Gesture Sensor service"); + mInputFilter = new GestureInputFilter(mContext); + mInputManager.registerSecondaryInputFilter(mInputFilter); + } + + public void setOnLongPressPendingIntent(PendingIntent pendingIntent) { + mInputFilter.setOnLongPressPendingIntent(pendingIntent); + } + + public void setOnDoubleClickPendingIntent(PendingIntent pendingIntent) { + mInputFilter.setOnDoubleClickPendingIntent(pendingIntent); + } +} diff --git a/services/java/com/android/server/pie/PieInputFilter.java b/services/java/com/android/server/pie/PieInputFilter.java index 999cf7284921f..01af4c62c6b24 100644 --- a/services/java/com/android/server/pie/PieInputFilter.java +++ b/services/java/com/android/server/pie/PieInputFilter.java @@ -226,7 +226,8 @@ public void filterInputEvent(InputEvent event, int policyFlags) throws RemoteExc Trace.traceBegin(Trace.TRACE_TAG_INPUT, "filterInputEvent"); } try { - if (event.getSource() != InputDevice.SOURCE_TOUCHSCREEN + if (((event.getSource() & InputDevice.SOURCE_TOUCHSCREEN) + != InputDevice.SOURCE_TOUCHSCREEN) || !(event instanceof MotionEvent)) { sendInputEvent(event, policyFlags); return; diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 09d02cd012899..bd0448ba943d5 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -6836,6 +6836,9 @@ private int installLocationPolicy(PackageInfoLite pkgLite, int flags) { // reader synchronized (mPackages) { PackageParser.Package pkg = mPackages.get(packageName); + if (pkgLite.isTheme) { + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } if (pkg != null) { if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { // Check for downgrading. @@ -7008,7 +7011,7 @@ public void handleStartCopy() throws RemoteException { loc = installLocationPolicy(pkgLite, flags); if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; - } else if (!onSd && !onInt) { + } else if ((!onSd && !onInt) || pkgLite.isTheme) { // Override install location with flags if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { // Set the flag to install on external media. @@ -7020,6 +7023,9 @@ public void handleStartCopy() throws RemoteException { flags |= PackageManager.INSTALL_INTERNAL; flags &= ~PackageManager.INSTALL_EXTERNAL; } + if (pkgLite.isTheme) { + flags &= ~PackageManager.INSTALL_FORWARD_LOCK; + } } } } diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index ec6f52a371429..ee5a5084d62b7 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -803,6 +803,7 @@ private void updatePowerState() { if (!mElectronBeamOffAnimator.isStarted()) { if (mPowerState.getElectronBeamLevel() == 0.0f) { setScreenOn(false); + unblockScreenOn(); } else if (mPowerState.prepareElectronBeam( mElectronBeamFadesConfig ? ElectronBeam.MODE_FADE : diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java index 379e704ab1d9e..69fa83cbcca75 100644 --- a/services/java/com/android/server/power/ElectronBeam.java +++ b/services/java/com/android/server/power/ElectronBeam.java @@ -31,6 +31,7 @@ import android.opengl.GLES10; import android.opengl.GLES11Ext; import android.os.Looper; +import android.os.SystemProperties; import android.util.FloatMath; import android.util.Slog; import android.view.Display; @@ -90,6 +91,8 @@ final class ElectronBeam { private EGLSurface mEglSurface; private boolean mSurfaceVisible; private float mSurfaceAlpha; + private final int mHWRotation; + private final boolean mSwapNeeded; // Texture names. We only use one texture, which contains the screenshot. private final int[] mTexNames = new int[1]; @@ -119,6 +122,8 @@ final class ElectronBeam { public ElectronBeam(DisplayManagerService displayManager) { mDisplayManager = displayManager; + mHWRotation = Integer.parseInt(SystemProperties.get("ro.sf.hwrotation", "0")) / 90; + mSwapNeeded = mHWRotation % 2 == 1; } /** @@ -139,8 +144,14 @@ public boolean prepare(int mode) { // This is not expected to change while the electron beam surface is showing. DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY); mDisplayLayerStack = displayInfo.layerStack; - mDisplayWidth = displayInfo.getNaturalWidth(); - mDisplayHeight = displayInfo.getNaturalHeight(); + + if (mSwapNeeded) { + mDisplayWidth = displayInfo.getNaturalHeight(); + mDisplayHeight = displayInfo.getNaturalWidth(); + } else { + mDisplayWidth = displayInfo.getNaturalWidth(); + mDisplayHeight = displayInfo.getNaturalHeight(); + } // Prepare the surface for drawing. if (!tryPrepare()) { @@ -287,17 +298,17 @@ private void drawVStretch(float stretch) { GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); // draw the red plane - setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar); + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar, mSwapNeeded); GLES10.glColorMask(true, false, false, true); GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); // draw the green plane - setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag, mSwapNeeded); GLES10.glColorMask(false, true, false, true); GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); // draw the blue plane - setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab); + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab, mSwapNeeded); GLES10.glColorMask(false, false, true, true); GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); @@ -337,7 +348,7 @@ private void drawHStretch(float stretch) { GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); // draw narrow fading white line - setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag, mSwapNeeded); GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f); GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); @@ -346,17 +357,33 @@ private void drawHStretch(float stretch) { } } - private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { - final float w = dw + (dw * a); - final float h = dh - (dh * a); + private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a, + boolean swap) { + final float w; + final float h; + if (swap) { + w = dw - (dw * a); + h = dh + (dh * a); + } else { + w = dw + (dw * a); + h = dh - (dh * a); + } final float x = (dw - w) * 0.5f; final float y = (dh - h) * 0.5f; setQuad(vtx, x, y, w, h); } - private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { - final float w = dw + (dw * a); - final float h = 1.0f; + private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a, + boolean swap) { + final float w; + final float h; + if (swap) { + w = 1.0f; + h = dh + (dh * a); + } else { + w = dw + (dw * a); + h = 1.0f; + } final float x = (dw - w) * 0.5f; final float y = (dh - h) * 0.5f; setQuad(vtx, x, y, w, h); @@ -526,7 +553,8 @@ private boolean createSurface() { mSurface = new Surface(); mSurface.copyFrom(mSurfaceControl); - mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManager, mSurfaceControl); + mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManager, mSurfaceControl, + mHWRotation); mSurfaceLayout.onDisplayTransaction(); } finally { SurfaceControl.closeTransaction(); @@ -686,11 +714,14 @@ public void dump(PrintWriter pw) { private static final class NaturalSurfaceLayout implements DisplayTransactionListener { private final DisplayManagerService mDisplayManager; private SurfaceControl mSurfaceControl; + private final int mHWRotation; - public NaturalSurfaceLayout(DisplayManagerService displayManager, SurfaceControl surfaceControl) { + public NaturalSurfaceLayout(DisplayManagerService displayManager, + SurfaceControl surfaceControl, int hwRotation) { mDisplayManager = displayManager; mSurfaceControl = surfaceControl; mDisplayManager.registerDisplayTransactionListener(this); + mHWRotation = hwRotation; } public void dispose() { @@ -708,7 +739,7 @@ public void onDisplayTransaction() { } DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY); - switch (displayInfo.rotation) { + switch ((displayInfo.rotation + mHWRotation) % 4) { case Surface.ROTATION_0: mSurfaceControl.setPosition(0, 0); mSurfaceControl.setMatrix(1, 0, 0, 1); diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java index aee7a39dfd283..66d0978c36b99 100644 --- a/services/java/com/android/server/power/ShutdownThread.java +++ b/services/java/com/android/server/power/ShutdownThread.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2008 The Android Open Source Project * Copyright (C) 2013 The CyanogenMod Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,12 +47,15 @@ import android.os.storage.IMountService; import android.os.storage.IMountShutdownObserver; import android.provider.Settings; +import android.telephony.MSimTelephonyManager; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.msim.ITelephonyMSim; import android.util.Log; import android.view.WindowManager; import android.view.KeyEvent; +import java.lang.reflect.Method; public final class ShutdownThread extends Thread { // constants @@ -493,10 +499,28 @@ public void run() { } try { - radioOff = phone == null || !phone.isRadioOn(); - if (!radioOff) { - Log.w(TAG, "Turning off radio..."); - phone.setRadio(false); + radioOff = true; + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + final ITelephonyMSim mphone = ITelephonyMSim.Stub.asInterface( + ServiceManager.checkService("phone_msim")); + if (mphone != null) { + //radio off indication should be sent for both subscriptions + //in case of DSDS. + for (int i = 0; i < MSimTelephonyManager.getDefault(). + getPhoneCount(); i++) { + radioOff = radioOff && !mphone.isRadioOn(i); + if (mphone.isRadioOn(i)) { + Log.w(TAG, "Turning off radio on Subscription :" + i); + mphone.setRadio(false, i); + } + } + } + } else { + radioOff = phone == null || !phone.isRadioOn(); + if (!radioOff) { + Log.w(TAG, "Turning off radio..."); + phone.setRadio(false); + } } } catch (RemoteException ex) { Log.e(TAG, "RemoteException during radio shutdown", ex); @@ -519,7 +543,18 @@ public void run() { } if (!radioOff) { try { - radioOff = !phone.isRadioOn(); + boolean subRadioOff = true; + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + final ITelephonyMSim mphone = ITelephonyMSim.Stub.asInterface( + ServiceManager.checkService("phone_msim")); + for (int i = 0; i < MSimTelephonyManager.getDefault(). + getPhoneCount(); i++) { + subRadioOff = subRadioOff && !mphone.isRadioOn(i); + } + radioOff = subRadioOff; + } else { + radioOff = !phone.isRadioOn(); + } } catch (RemoteException ex) { Log.e(TAG, "RemoteException during radio shutdown", ex); radioOff = true; @@ -568,6 +603,9 @@ public void run() { * @param reason reason for reboot */ public static void rebootOrShutdown(boolean reboot, String reason) { + // Oem specific shutdown + deviceRebootOrShutdown(reboot, reason); + if (reboot) { Log.i(TAG, "Rebooting, reason: " + reason); try { @@ -596,4 +634,28 @@ public static void rebootOrShutdown(boolean reboot, String reason) { Log.i(TAG, "Performing low-level shutdown..."); PowerManagerService.lowLevelShutdown(); } + + private static void deviceRebootOrShutdown(boolean reboot, String reason) { + + Class cl; + String deviceShutdownClassName = "com.android.server.power.ShutdownOem"; + + try{ + cl = Class.forName(deviceShutdownClassName); + Method m; + try { + m = cl.getMethod("rebootOrShutdown", new Class[]{boolean.class, String.class}); + m.invoke(cl.newInstance(), reboot, reason); + + } catch (NoSuchMethodException ex) { + //Method not found. + } catch (Exception ex) { + //Unknown exception + } + }catch(ClassNotFoundException e){ + //Classnotfound! + }catch(Exception e){ + //Unknown exception + } + } } diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index a0cff0bc6dc2e..db6b9403f242d 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -274,7 +274,7 @@ public void checkAndStartWifi() { // If we are already disabled (could be due to airplane mode), avoid changing persist // state here - if (wifiEnabled) setWifiEnabled(wifiEnabled); + if (wifiEnabled) setWifiEnabled(mContext.getBasePackageName(), wifiEnabled); mWifiWatchdogStateMachine = WifiWatchdogStateMachine. makeWifiWatchdogStateMachine(mContext); @@ -332,8 +332,14 @@ private void enforceConnectivityInternalPermission() { * @return {@code true} if the enable/disable operation was * started or is already in the queue. */ - public synchronized boolean setWifiEnabled(boolean enable) { + public synchronized boolean setWifiEnabled(String callingPackage, boolean enable) { enforceChangePermission(); + + int uid = Binder.getCallingUid(); + if (mAppOps.noteOp(AppOpsManager.OP_WIFI_CHANGE, uid, callingPackage) + != AppOpsManager.MODE_ALLOWED) + return false; + Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); if (DBG) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 9840418680cd0..2124c5974d83b 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5442,11 +5442,6 @@ public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, ws.isDisplayedLw()) { screenshotReady = true; } - - if (fullscreen) { - // No point in continuing down through windows. - break; - } } if (appToken != null && appWin == null) { diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index c07174baf4c37..8082e297b12fc 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -1233,7 +1233,9 @@ public void prepareSurfaceLocked(final boolean recoveringMemory) { // introduce a potential glitch if the window // becomes unhidden before it has drawn for the // new orientation. - if (w.mOrientationChanging) { + if (!w.isDrawnLw()) { + displayed = true; + } else if (w.mOrientationChanging) { w.mOrientationChanging = false; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); diff --git a/telephony/java/android/telephony/MSimTelephonyManager.java b/telephony/java/android/telephony/MSimTelephonyManager.java new file mode 100644 index 0000000000000..bf1a999bfe38f --- /dev/null +++ b/telephony/java/android/telephony/MSimTelephonyManager.java @@ -0,0 +1,1134 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * 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.telephony; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemProperties; +import android.text.TextUtils; + +import com.android.internal.telephony.msim.ITelephonyMSim; +import com.android.internal.telephony.ITelephonyRegistryMSim; +import com.android.internal.telephony.msim.IPhoneSubInfoMSim; +import com.android.internal.telephony.MSimConstants; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyProperties; + +import java.util.List; + +/** + * Provides access to information about the telephony services on + * the device. Applications can use the methods in this class to + * determine telephony services and states, as well as to access some + * types of subscriber information. Applications can also register + * a listener to receive notification of telephony state changes. + *

    + * You do not instantiate this class directly; instead, you retrieve + * a reference to an instance through + * {@link android.content.Context#getSystemService + * Context.getSystemService(Context.MSIM_TELEPHONY_SERVICE)}. + *

    + * Note that access to some telephony information is + * permission-protected. Your application cannot access the protected + * information unless it has the appropriate permissions declared in + * its manifest file. Where permissions apply, they are noted in the + * the methods through which you access the protected information. + * @hide + */ +public class MSimTelephonyManager { + /** @hide */ + private static Context sContext; + /** @hide */ + protected static ITelephonyRegistryMSim sRegistryMsim; + + protected static String multiSimConfig = + SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG); + + /** Enum indicating multisim variants + * DSDS - Dual SIM Dual Standby + * DSDA - Dual SIM Dual Active + * TSTS - Triple SIM Triple Standby + **/ + public enum MultiSimVariants { + DSDS, + DSDA, + TSTS, + UNKNOWN + }; + + /** @hide */ + public MSimTelephonyManager(Context context) { + if (sContext == null) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + sContext = appContext; + } else { + sContext = context; + } + + sRegistryMsim = ITelephonyRegistryMSim.Stub.asInterface(ServiceManager.getService( + "telephony.msim.registry")); + } + } + + /** @hide */ + private MSimTelephonyManager() { + } + + private static MSimTelephonyManager sInstance = new MSimTelephonyManager(); + + /** @hide + /* @deprecated - use getSystemService as described above */ + public static MSimTelephonyManager getDefault() { + return sInstance; + } + + /** {@hide} */ + public static MSimTelephonyManager from(Context context) { + return (MSimTelephonyManager) context.getSystemService(Context.MSIM_TELEPHONY_SERVICE); + } + + public boolean isMultiSimEnabled() { + return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") || + multiSimConfig.equals("tsts")); + } + + /** + * Returns the multi SIM variant + * Returns DSDS for Dual SIM Dual Standby + * Returns DSDA for Dual SIM Dual Active + * Returns TSTS for Triple SIM Triple Standby + * Returns UNKNOWN for others + */ + public MultiSimVariants getMultiSimConfiguration() { + if (multiSimConfig.equals("dsds")) { + return MultiSimVariants.DSDS; + } else if (multiSimConfig.equals("dsda")) { + return MultiSimVariants.DSDA; + } else if (multiSimConfig.equals("tsts")) { + return MultiSimVariants.TSTS; + } else { + return MultiSimVariants.UNKNOWN; + } + } + + + /** + * Returns the number of phones available. + * Returns 1 for Single standby mode (Single SIM functionality) + * Returns 2 for Dual standby mode.(Dual SIM functionality) + */ + public int getPhoneCount() { + int phoneCount = 1; + switch (getMultiSimConfiguration()) { + case DSDS: + case DSDA: + phoneCount = MSimConstants.MAX_PHONE_COUNT_DUAL_SIM; + break; + case TSTS: + phoneCount = MSimConstants.MAX_PHONE_COUNT_TRI_SIM; + break; + } + return phoneCount; + } + + /** + * Returns the software version number for the device, for example, + * the IMEI/SV for GSM phones. Return null if the software version is + * not available. + * + *

    Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + */ + public String getDeviceSoftwareVersion(int subscription) { + try { + return getMSimSubscriberInfo().getDeviceSvn(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the unique device ID of a subscription, for example, the IMEI for + * GSM and the MEID for CDMA phones. Return null if device ID is not available. + * + *

    Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param subscription of which deviceID is returned + */ + public String getDeviceId(int subscription) { + + try { + return getMSimSubscriberInfo().getDeviceId(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns a constant indicating the device phone type for a subscription. + * + * @see #PHONE_TYPE_NONE + * @see #PHONE_TYPE_GSM + * @see #PHONE_TYPE_CDMA + * + * @param subscription for which phone type is returned + * @hide + */ + public int getCurrentPhoneType(int subscription) { + + try{ + ITelephonyMSim telephony = getITelephonyMSim(); + if (telephony != null) { + return telephony.getActivePhoneType(subscription); + } else { + // This can happen when the ITelephonyMSim interface is not up yet. + return getPhoneTypeFromProperty(subscription); + } + } catch (RemoteException ex) { + // This shouldn't happen in the normal case, as a backup we + // read from the system property. + return getPhoneTypeFromProperty(subscription); + } catch (NullPointerException ex) { + // This shouldn't happen in the normal case, as a backup we + // read from the system property. + return getPhoneTypeFromProperty(subscription); + } + } + + /** + * Returns a constant indicating the device phone type. This + * indicates the type of radio used to transmit voice calls. + * + * @see #PHONE_TYPE_NONE + * @see #PHONE_TYPE_GSM + * @see #PHONE_TYPE_CDMA + * @see #PHONE_TYPE_SIP + * + * @param subscription for which phone type is returned + * @hide + */ + public int getPhoneType(int subscription) { + if (!TelephonyManager.getDefault().isVoiceCapable()) { + return TelephonyManager.PHONE_TYPE_NONE; + } + return getCurrentPhoneType(subscription); + } + + private int getPhoneTypeFromProperty(int subscription) { + String type = + getTelephonyProperty + (TelephonyProperties.CURRENT_ACTIVE_PHONE, subscription, null); + if (type != null) { + return (Integer.parseInt(type)); + } else { + return getPhoneTypeFromNetworkType(subscription); + } + } + + private int getPhoneTypeFromNetworkType(int subscription) { + // When the system property CURRENT_ACTIVE_PHONE, has not been set, + // use the system property for default network type. + // This is a fail safe, and can only happen at first boot. + String mode = getTelephonyProperty("ro.telephony.default_network", subscription, null); + if (mode != null) { + return TelephonyManager.getPhoneType(Integer.parseInt(mode)); + } + return TelephonyManager.PHONE_TYPE_NONE; + } + + // + // + // Current Network + // + // + + /** + * Returns the alphabetic name of current registered operator + * for a particular subscription. + *

    + * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + * @param subscription + */ + public String getNetworkOperatorName(int subscription) { + + return getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, + subscription, ""); + } + + /** + * Returns the numeric name (MCC+MNC) of current registered operator + * for a particular subscription. + *

    + * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + * + * @param subscription + */ + public String getNetworkOperator(int subscription) { + + return getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, + subscription, ""); + } + + /** + * Returns true if the device is considered roaming on the current + * network for a subscription. + *

    + * Availability: Only when user registered to a network. + * + * @param subscription + */ + public boolean isNetworkRoaming(int subscription) { + return "true".equals(getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, + subscription, null)); + } + + /** + * Returns the ISO country code equivalent of the current registered + * operator's MCC (Mobile Country Code). + *

    + * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + * + * @param subscription for which Network CountryIso is returned + * @hide + */ + public String getNetworkCountryIso(int subscription) { + return getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, + subscription, ""); + } + + /** + * Returns a constant indicating the radio technology (network type) + * currently in use on the device for data transmission for a subscription + * @return the network type + * + * @param subscription for which network type is returned + * + * @see #NETWORK_TYPE_UNKNOWN + * @see #NETWORK_TYPE_GPRS + * @see #NETWORK_TYPE_EDGE + * @see #NETWORK_TYPE_UMTS + * @see #NETWORK_TYPE_HSDPA + * @see #NETWORK_TYPE_HSUPA + * @see #NETWORK_TYPE_HSPA + * @see #NETWORK_TYPE_CDMA + * @see #NETWORK_TYPE_EVDO_0 + * @see #NETWORK_TYPE_EVDO_A + * @see #NETWORK_TYPE_EVDO_B + * @see #NETWORK_TYPE_1xRTT + * @see #NETWORK_TYPE_IDEN + * @see #NETWORK_TYPE_LTE + * @see #NETWORK_TYPE_EHRPD + * @see #NETWORK_TYPE_HSPAP + */ + public int getNetworkType(int subscription) { + try { + ITelephonyMSim iTelephony = getITelephonyMSim(); + if (iTelephony != null) { + return iTelephony.getNetworkType(subscription); + } else { + // This can happen when the ITelephony interface is not up yet. + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + } catch(RemoteException ex) { + // This shouldn't happen in the normal case + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + } + + /** + * Returns a constant indicating the radio technology (network type) + * currently in use on the device for data transmission. + * @return the network type + * + * @see #NETWORK_TYPE_UNKNOWN + * @see #NETWORK_TYPE_GPRS + * @see #NETWORK_TYPE_EDGE + * @see #NETWORK_TYPE_UMTS + * @see #NETWORK_TYPE_HSDPA + * @see #NETWORK_TYPE_HSUPA + * @see #NETWORK_TYPE_HSPA + * @see #NETWORK_TYPE_CDMA + * @see #NETWORK_TYPE_EVDO_0 + * @see #NETWORK_TYPE_EVDO_A + * @see #NETWORK_TYPE_EVDO_B + * @see #NETWORK_TYPE_1xRTT + * @see #NETWORK_TYPE_IDEN + * @see #NETWORK_TYPE_LTE + * @see #NETWORK_TYPE_EHRPD + * @see #NETWORK_TYPE_HSPAP + * @see #NETWORK_TYPE_TD_SCDMA + * + * @hide + */ + public int getDataNetworkType(int subscription) { + try{ + ITelephonyMSim telephony = getITelephonyMSim(); + if (telephony != null) { + return telephony.getDataNetworkType(subscription); + } else { + // This can happen when the ITelephonyMSim interface is not up yet. + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + } catch(RemoteException ex) { + // This shouldn't happen in the normal case + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + } + + /** + * Returns the NETWORK_TYPE_xxxx for voice + * + * @hide + */ + public int getVoiceNetworkType(int subscription) { + try{ + ITelephonyMSim telephony = getITelephonyMSim(); + if (telephony != null) { + return telephony.getVoiceNetworkType(subscription); + } else { + // This can happen when the ITelephonyMSim interface is not up yet. + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + } catch(RemoteException ex) { + // This shouldn't happen in the normal case + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + } + + /** + * Returns a string representation of the radio technology (network type) + * currently in use on the device. + * @param subscription for which network type is returned + * @return the name of the radio technology + * + * @hide pending API council review + */ + public String getNetworkTypeName(int subscription) { + return TelephonyManager.getNetworkTypeName(getNetworkType(subscription)); + } + + // + // + // SIM Card + // + // + + /** + * @return true if a ICC card is present for a subscription + * + * @param subscription for which icc card presence is checked + */ + public boolean hasIccCard(int subscription) { + + try { + return getITelephonyMSim().hasIccCard(subscription); + } catch (RemoteException ex) { + // Assume no ICC card if remote exception which shouldn't happen + return false; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return false; + } + } + + /** + * Returns a constant indicating the state of the + * device SIM card in a slot. + * + * @param slotId + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_READY + */ + public int getSimState(int slotId) { + String prop = + getTelephonyProperty(TelephonyProperties.PROPERTY_SIM_STATE, slotId, ""); + if ("ABSENT".equals(prop)) { + return TelephonyManager.SIM_STATE_ABSENT; + } + else if ("PIN_REQUIRED".equals(prop)) { + return TelephonyManager.SIM_STATE_PIN_REQUIRED; + } + else if ("PUK_REQUIRED".equals(prop)) { + return TelephonyManager.SIM_STATE_PUK_REQUIRED; + } + else if ("NETWORK_LOCKED".equals(prop)) { + return TelephonyManager.SIM_STATE_NETWORK_LOCKED; + } + else if ("READY".equals(prop)) { + return TelephonyManager.SIM_STATE_READY; + } + else { + return TelephonyManager.SIM_STATE_UNKNOWN; + } + } + + /** + * Returns the MCC+MNC (mobile country code + mobile network code) of the + * provider of the SIM. 5 or 6 decimal digits. + *

    + * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + * + * @param subscription for which SimOperator is returned + * @hide + */ + public String getSimOperator(int subscription) { + return getTelephonyProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, + subscription, ""); + } + + /** + * Returns the Service Provider Name (SPN). + *

    + * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + * + * @param subscription for which SimOperatorName is returned + * @hide + */ + public String getSimOperatorName(int subscription) { + return getTelephonyProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, + subscription, ""); + } + + /** + * Returns the ISO country code equivalent for the SIM provider's country code. + * + * @param subscription for which SimCountryIso is returned + * @hide + */ + public String getSimCountryIso(int subscription) { + return getTelephonyProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, + subscription, ""); + } + + /** + * Returns the serial number for the given subscription, if applicable. Return null if it is + * unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + */ + public String getSimSerialNumber(int subscription) { + try { + return getMSimSubscriberInfo().getIccSerialNumber(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Return if the current radio is LTE on CDMA. This + * is a tri-state return value as for a period of time + * the mode may be unknown. + * + * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE} + * or {@link Phone#LTE_ON_CDMA_TRUE} + * + * @hide + */ + public int getLteOnCdmaMode(int subscription) { + try { + return getITelephonyMSim().getLteOnCdmaMode(subscription); + } catch (RemoteException ex) { + // Assume no ICC card if remote exception which shouldn't happen + return PhoneConstants.LTE_ON_CDMA_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return PhoneConstants.LTE_ON_CDMA_UNKNOWN; + } + } + + // + // + // Subscriber Info + // + // + + /** + * Returns the unique subscriber ID, for example, the IMSI for a GSM phone + * for a subscription. + * Return null if it is unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param subscription whose subscriber id is returned + */ + public String getSubscriberId(int subscription) { + try { + return getMSimSubscriberInfo().getSubscriberId(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the phone number string for line 1, for example, the MSISDN + * for a GSM phone for a particular subscription. Return null if it is unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param subscription whose phone number for line 1 is returned + */ + public String getLine1Number(int subscription) { + try { + return getMSimSubscriberInfo().getLine1Number(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the alphabetic identifier associated with the line 1 number + * for a subscription. + * Return null if it is unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * @param subscription whose alphabetic identifier associated with line 1 is returned + * @hide + * nobody seems to call this. + */ + public String getLine1AlphaTag(int subscription) { + try { + return getMSimSubscriberInfo().getLine1AlphaTag(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the MSISDN string. + * for a GSM phone. Return null if it is unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param subscription for which msisdn is returned + * @hide + */ + public String getMsisdn(int subscription) { + try { + return getMSimSubscriberInfo().getMsisdn(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the voice mail number for a subscription. + * Return null if it is unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * @param subscription whose voice mail number is returned + */ + public String getVoiceMailNumber(int subscription) { + try { + return getMSimSubscriberInfo().getVoiceMailNumber(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the complete voice mail number. Return null if it is unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED} + * + * @param subscription + * @hide + */ + public String getCompleteVoiceMailNumber(int subscription) { + try { + return getMSimSubscriberInfo().getCompleteVoiceMailNumber(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + + /** + * Returns the voice mail count for a subscription. Return 0 if unavailable. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * @param subscription whose voice message count is returned + * @hide + */ + public int getVoiceMessageCount(int subscription) { + try { + return getITelephonyMSim().getVoiceMessageCount(subscription); + } catch (RemoteException ex) { + return 0; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return 0; + } + } + + /** + * Retrieves the alphabetic identifier associated with the voice + * mail number for a subscription. + *

    + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * @param subscription whose alphabetic identifier associated with the + * voice mail number is returned + */ + public String getVoiceMailAlphaTag(int subscription) { + try { + return getMSimSubscriberInfo().getVoiceMailAlphaTag(subscription); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * @hide + */ + protected IPhoneSubInfoMSim getMSimSubscriberInfo() { + // get it each time because that process crashes a lot + return IPhoneSubInfoMSim.Stub.asInterface(ServiceManager.getService("iphonesubinfo_msim")); + } + + /** + * Returns a constant indicating the call state (cellular) on the device + * for a subscription. + * + * @param subscription whose call state is returned + */ + public int getCallState(int subscription) { + try { + return getITelephonyMSim().getCallState(subscription); + } catch (RemoteException ex) { + // the phone process is restarting. + return TelephonyManager.CALL_STATE_IDLE; + } catch (NullPointerException ex) { + // the phone process is restarting. + return TelephonyManager.CALL_STATE_IDLE; + } + } + + /** + * Returns a constant indicating the type of activity on a data connection + * (cellular). + * + * @see #DATA_ACTIVITY_NONE + * @see #DATA_ACTIVITY_IN + * @see #DATA_ACTIVITY_OUT + * @see #DATA_ACTIVITY_INOUT + * @see #DATA_ACTIVITY_DORMANT + */ + public int getDataActivity() { + try { + return getITelephonyMSim().getDataActivity(); + } catch (RemoteException ex) { + // the phone process is restarting. + return TelephonyManager.DATA_ACTIVITY_NONE; + } catch (NullPointerException ex) { + // the phone process is restarting. + return TelephonyManager.DATA_ACTIVITY_NONE; + } + } + + /** + * Returns a constant indicating the current data connection state + * (cellular). + * + * @see #DATA_DISCONNECTED + * @see #DATA_CONNECTING + * @see #DATA_CONNECTED + * @see #DATA_SUSPENDED + */ + public int getDataState() { + try { + return getITelephonyMSim().getDataState(); + } catch (RemoteException ex) { + // the phone process is restarting. + return TelephonyManager.DATA_DISCONNECTED; + } catch (NullPointerException ex) { + return TelephonyManager.DATA_DISCONNECTED; + } + } + + private ITelephonyMSim getITelephonyMSim() { + return ITelephonyMSim.Stub.asInterface(ServiceManager.getService( + Context.MSIM_TELEPHONY_SERVICE)); + } + + // + // + // PhoneStateListener + // + // + + /** + * Registers a listener object to receive notification of changes + * in specified telephony states. + *

    + * To register a listener, pass a {@link PhoneStateListener} + * and specify at least one telephony state of interest in + * the events argument. + * + * At registration, and when a specified telephony state + * changes, the telephony manager invokes the appropriate + * callback method on the listener object and passes the + * current (udpated) values. + *

    + * To unregister a listener, pass the listener object and set the + * events argument to + * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0). + * + * @param listener The {@link PhoneStateListener} object to register + * (or unregister) + * @param events The telephony state(s) of interest to the listener, + * as a bitwise-OR combination of {@link PhoneStateListener} + * LISTEN_ flags. + */ + public void listen(PhoneStateListener listener, int events) { + String pkgForDebug = sContext != null ? sContext.getPackageName() : ""; + try { + Boolean notifyNow = (getITelephonyMSim() != null); + sRegistryMsim.listen(pkgForDebug, listener.callback, events, notifyNow, + listener.mSubscription); + } catch (RemoteException ex) { + // system process dead + } catch (NullPointerException ex) { + // system process dead + } + } + + /** + * Returns the CDMA ERI icon index to display for a subscription + * + * @hide + */ + public int getCdmaEriIconIndex(int subscription) { + try { + return getITelephonyMSim().getCdmaEriIconIndex(subscription); + } catch (RemoteException ex) { + // the phone process is restarting. + return -1; + } catch (NullPointerException ex) { + return -1; + } + } + + /** + * Returns the CDMA ERI icon mode for a subscription. + * 0 - ON + * 1 - FLASHING + * + * @hide + */ + public int getCdmaEriIconMode(int subscription) { + try { + return getITelephonyMSim().getCdmaEriIconMode(subscription); + } catch (RemoteException ex) { + // the phone process is restarting. + return -1; + } catch (NullPointerException ex) { + return -1; + } + } + + /** + * Returns the CDMA ERI text, of a subscription + * + * @hide + */ + public String getCdmaEriText(int subscription) { + try { + return getITelephonyMSim().getCdmaEriText(subscription); + } catch (RemoteException ex) { + // the phone process is restarting. + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns all observed cell information of the device. + * + * @return List of CellInfo or null if info unavailable + * for subscription. + * + *

    Requires Permission: + * (@link android.Manifest.permission#ACCESS_COARSE_UPDATES} + * + * @hide pending API review + */ + public List getAllCellInfo() { + try { + return getITelephonyMSim().getAllCellInfo(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Sets the telephony property with the value specified. + * + * @hide + */ + public static void setTelephonyProperty(String property, int index, String value) { + String propVal = ""; + String p[] = null; + String prop = SystemProperties.get(property); + + if (prop != null) { + p = prop.split(","); + } + + if (index < 0) return; + + for (int i = 0; i < index; i++) { + String str = ""; + if ((p != null) && (i < p.length)) { + str = p[i]; + } + propVal = propVal + str + ","; + } + + propVal = propVal + value; + if (p != null) { + for (int i = index+1; i < p.length; i++) { + propVal = propVal + "," + p[i]; + } + } + SystemProperties.set(property, propVal); + } + + /** + * Gets the telephony property. + * + * @hide + */ + public static String getTelephonyProperty(String property, int index, String defaultVal) { + String propVal = null; + String prop = SystemProperties.get(property); + + if ((prop != null) && (prop.length() > 0)) { + String values[] = prop.split(","); + if ((index >= 0) && (index < values.length) && (values[index] != null)) { + propVal = values[index]; + } + } + return propVal == null ? defaultVal : propVal; + } + + /** + * Returns Default subscription. + * Returns default value 0, if default subscription is not available + */ + public int getDefaultSubscription() { + try { + return getITelephonyMSim().getDefaultSubscription(); + } catch (RemoteException ex) { + return MSimConstants.DEFAULT_SUBSCRIPTION; + } catch (NullPointerException ex) { + return MSimConstants.DEFAULT_SUBSCRIPTION; + } + } + + /** + * Returns the designated data subscription. + */ + public int getPreferredDataSubscription() { + try { + return getITelephonyMSim().getPreferredDataSubscription(); + } catch (RemoteException ex) { + return MSimConstants.DEFAULT_SUBSCRIPTION; + } catch (NullPointerException ex) { + return MSimConstants.DEFAULT_SUBSCRIPTION; + } + } + + /** + * Sets the designated data subscription. + */ + public boolean setPreferredDataSubscription(int subscription) { + try { + return getITelephonyMSim().setPreferredDataSubscription(subscription); + } catch (RemoteException ex) { + return false; + } catch (NullPointerException ex) { + return false; + } + } + + /** + * Returns the preferred voice subscription. + */ + public int getPreferredVoiceSubscription() { + try { + return getITelephonyMSim().getPreferredVoiceSubscription(); + } catch (RemoteException ex) { + return MSimConstants.DEFAULT_SUBSCRIPTION; + } catch (NullPointerException ex) { + return MSimConstants.DEFAULT_SUBSCRIPTION; + } + } + + /** + * Convenience function for retrieving a value from the secure settings + * value list as an integer. Note that internally setting values are + * always stored as strings; this function converts the string to an + * integer for you. + *

    + * This version does not take a default value. If the setting has not + * been set, or the string value is not a number, + * it throws {@link SettingNotFoundException}. + * + * @param cr The ContentResolver to access. + * @param name The name of the setting to retrieve. + * @param index The index of the list + * + * @throws SettingNotFoundException Thrown if a setting by the given + * name can't be found or the setting value is not an integer. + * + * @return The value at the given index of settings. + * @hide + */ + public static int getIntAtIndex(android.content.ContentResolver cr, + String name, int index) + throws android.provider.Settings.SettingNotFoundException { + String v = android.provider.Settings.Global.getString(cr, name); + if (v != null) { + String valArray[] = v.split(","); + if ((index >= 0) && (index < valArray.length) && (valArray[index] != null)) { + try { + return Integer.parseInt(valArray[index]); + } catch (NumberFormatException e) { + //Log.e(TAG, "Exception while parsing Integer: ", e); + } + } + } + throw new android.provider.Settings.SettingNotFoundException(name); + } + + /** + * Convenience function for updating settings value as coma separated + * integer values. This will either create a new entry in the table if the + * given name does not exist, or modify the value of the existing row + * with that name. Note that internally setting values are always + * stored as strings, so this function converts the given value to a + * string before storing it. + * + * @param cr The ContentResolver to access. + * @param name The name of the setting to modify. + * @param index The index of the list + * @param value The new value for the setting to be added to the list. + * @return true if the value was set, false on database errors + * @hide + */ + public static boolean putIntAtIndex(android.content.ContentResolver cr, + String name, int index, int value) { + String data = ""; + String valArray[] = null; + String v = android.provider.Settings.Global.getString(cr, name); + + if (v != null) { + valArray = v.split(","); + } + + // Copy the elements from valArray till index + for (int i = 0; i < index; i++) { + String str = ""; + if ((valArray != null) && (i < valArray.length)) { + str = valArray[i]; + } + data = data + str + ","; + } + + data = data + value; + + // Copy the remaining elements from valArray if any. + if (valArray != null) { + for (int i = index+1; i < valArray.length; i++) { + data = data + "," + valArray[i]; + } + } + return android.provider.Settings.Global.putString(cr, name, data); + } +} diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 3ae210636f84f..0a0bbd184bec3 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +29,7 @@ import android.content.Intent; import android.database.Cursor; import android.location.CountryDetector; +import android.location.Country; import android.net.Uri; import android.os.SystemProperties; import android.provider.Contacts; @@ -36,6 +40,7 @@ import android.telephony.Rlog; import android.util.SparseIntArray; +import static com.android.internal.telephony.MSimConstants.SUBSCRIPTION_KEY; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; @@ -166,6 +171,12 @@ public static String getNumberFromIntent(Intent intent, Context context) { // TODO: We don't check for SecurityException here (requires // CALL_PRIVILEGED permission). if (scheme.equals("voicemail")) { + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + int subscription = intent.getIntExtra(SUBSCRIPTION_KEY, + MSimTelephonyManager.getDefault().getDefaultSubscription()); + return MSimTelephonyManager.getDefault() + .getCompleteVoiceMailNumber(subscription); + } return TelephonyManager.getDefault().getCompleteVoiceMailNumber(); } @@ -1688,9 +1699,27 @@ private static boolean isEmergencyNumberInternal(String number, // to the list. number = extractNetworkPortionAlt(number); - // retrieve the list of emergency numbers - // check read-write ecclist property first - String numbers = SystemProperties.get("ril.ecclist"); + String numbers = ""; + for (int i = 0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) { + // retrieve the list of emergency numbers + // check read-write ecclist property first + String ecclist = (i == 0) ? "ril.ecclist" : ("ril.ecclist" + i); + + if (!TextUtils.isEmpty(numbers)) { + numbers = numbers + ","; + } + numbers = numbers + SystemProperties.get(ecclist); + } + + // Additional emergency numbers can be set by a system property + String additionalEcclist = SystemProperties.get("ro.ril.ext.ecclist", null); + if (!TextUtils.isEmpty(additionalEcclist)) { + if (!TextUtils.isEmpty(numbers)) { + numbers = numbers + ","; + } + numbers = numbers + additionalEcclist; + } + if (TextUtils.isEmpty(numbers)) { // then read-only ecclist property since old RIL only uses this numbers = SystemProperties.get("ro.ril.ecclist"); @@ -1802,16 +1831,21 @@ public static boolean isPotentialLocalEmergencyNumber(String number, Context con private static boolean isLocalEmergencyNumberInternal(String number, Context context, boolean useExactMatch) { - String countryIso; + String countryIso = null; + Country country; CountryDetector detector = (CountryDetector) context.getSystemService( Context.COUNTRY_DETECTOR); - if (detector != null) { - countryIso = detector.detectCountry().getCountryIso(); + if ((detector != null) && ((country = detector.detectCountry()) != null)) { + countryIso = country.getCountryIso(); } else { Locale locale = context.getResources().getConfiguration().locale; - countryIso = locale.getCountry(); - Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: " - + countryIso); + if(locale != null) { + countryIso = locale.getCountry(); + Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: " + + countryIso); + } else { + countryIso = "US"; //default value is "US" + } } return isEmergencyNumberInternal(number, countryIso, useExactMatch); } @@ -1831,7 +1865,13 @@ public static boolean isVoiceMailNumber(String number) { String vmNumber; try { - vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(); + if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) { + int subscription = MSimTelephonyManager.getDefault() + .getPreferredVoiceSubscription(); + vmNumber = MSimTelephonyManager.getDefault().getVoiceMailNumber(subscription); + } else { + vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(); + } } catch (SecurityException ex) { return false; } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index ff77fc012f5ce..da9bdfb96285e 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +27,7 @@ import android.telephony.CellLocation; import android.telephony.CellInfo; import android.telephony.Rlog; +import android.telephony.MSimTelephonyManager; import com.android.internal.telephony.IPhoneStateListener; @@ -165,7 +169,22 @@ public class PhoneStateListener { */ public static final int LISTEN_CELL_INFO = 0x00000400; + /** + * Subscription used to listen to the phone state changes + * @hide + */ + protected int mSubscription = 0; + public PhoneStateListener() { + // If subscription is not passed set the default subscription. + mSubscription = MSimTelephonyManager.getDefault().getDefaultSubscription(); + } + + /** + * @hide + */ + public PhoneStateListener(int subscription) { + mSubscription = subscription; } /** diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index bcb3b024d6738..6754ce3060ecb 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -148,6 +148,11 @@ public class ServiceState implements Parcelable { * @hide */ public static final int RIL_RADIO_TECHNOLOGY_GSM = 16; + /** + * TD-SCDMA + * @hide + */ + public static final int RIL_RADIO_TECHNOLOGY_TD_SCDMA = 17; /** * Available registration states for GSM, UMTS and CDMA. @@ -183,7 +188,7 @@ public class ServiceState implements Parcelable { private int mSystemId; private int mCdmaRoamingIndicator; private int mCdmaDefaultRoamingIndicator; - private int mCdmaEriIconIndex; + private int mCdmaEriIconIndex = 1; //EriInfo.ROAMING_INDICATOR_OFF; private int mCdmaEriIconMode; /** @@ -538,6 +543,9 @@ public static String rilRadioTechnologyToString(int rt) { case RIL_RADIO_TECHNOLOGY_GSM: rtString = "GSM"; break; + case RIL_RADIO_TECHNOLOGY_TD_SCDMA: + rtString = "TD-SCDMA"; + break; default: rtString = "Unexpected"; Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt); @@ -804,6 +812,10 @@ private int rilRadioTechnologyToNetworkType(int rt) { return TelephonyManager.NETWORK_TYPE_HSPAP; case ServiceState.RIL_RADIO_TECHNOLOGY_DCHSPAP: return TelephonyManager.NETWORK_TYPE_DCHSPAP; + case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA: + return TelephonyManager.NETWORK_TYPE_TD_SCDMA; + case ServiceState.RIL_RADIO_TECHNOLOGY_GSM: + return TelephonyManager.NETWORK_TYPE_GSM; default: return TelephonyManager.NETWORK_TYPE_UNKNOWN; } @@ -854,7 +866,8 @@ public static boolean isGsm(int radioTechnology) { || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPAP || radioTechnology == RIL_RADIO_TECHNOLOGY_DCHSPAP - || radioTechnology == RIL_RADIO_TECHNOLOGY_GSM; + || radioTechnology == RIL_RADIO_TECHNOLOGY_GSM + || radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA; } /** @hide */ diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index a89f9ea9e9a31..fad5029562e39 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -63,6 +63,7 @@ public class SignalStrength implements Parcelable { private int mLteRsrq; private int mLteRssnr; private int mLteCqi; + private int mTdScdmaRscp; private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult @@ -102,6 +103,7 @@ public SignalStrength() { mLteRsrq = INVALID; mLteRssnr = INVALID; mLteCqi = INVALID; + mTdScdmaRscp = INVALID; isGsm = true; } @@ -126,6 +128,7 @@ public SignalStrength(boolean gsmFlag) { mLteRsrq = INVALID; mLteRssnr = INVALID; mLteCqi = INVALID; + mTdScdmaRscp = INVALID; isGsm = gsmFlag; } @@ -144,6 +147,22 @@ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate, lteRsrq, lteRssnr, lteCqi, gsmFlag); } + /** + * Constructor + * + * @hide + */ + public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate, + int cdmaDbm, int cdmaEcio, + int evdoDbm, int evdoEcio, int evdoSnr, + int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi, + int tdScdmaRscp, boolean gsmFlag) { + initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio, + evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp, + lteRsrq, lteRssnr, lteCqi, gsmFlag); + mTdScdmaRscp = tdScdmaRscp; + } + /** * Constructor * @@ -228,6 +247,7 @@ public void initialize(int gsmSignalStrength, int gsmBitErrorRate, mLteRsrq = lteRsrq; mLteRssnr = lteRssnr; mLteCqi = lteCqi; + mTdScdmaRscp = INVALID; isGsm = gsm; if (DBG) log("initialize: " + toString()); } @@ -248,6 +268,7 @@ protected void copyFrom(SignalStrength s) { mLteRsrq = s.mLteRsrq; mLteRssnr = s.mLteRssnr; mLteCqi = s.mLteCqi; + mTdScdmaRscp = s.mTdScdmaRscp; isGsm = s.isGsm; } @@ -271,6 +292,7 @@ public SignalStrength(Parcel in) { mLteRsrq = in.readInt(); mLteRssnr = in.readInt(); mLteCqi = in.readInt(); + mTdScdmaRscp = in.readInt(); isGsm = (in.readInt() != 0); } @@ -297,7 +319,7 @@ public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) { ss.mLteRsrq = in.readInt(); ss.mLteRssnr = in.readInt(); ss.mLteCqi = in.readInt(); - + ss.mTdScdmaRscp = in.readInt(); return ss; } @@ -317,6 +339,7 @@ public void writeToParcel(Parcel out, int flags) { out.writeInt(mLteRsrq); out.writeInt(mLteRssnr); out.writeInt(mLteCqi); + out.writeInt(mTdScdmaRscp); out.writeInt(isGsm ? 1 : 0); } @@ -372,6 +395,10 @@ public void validateInput() { mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID; mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300) && !(mLteRsrq == SignalStrength.INVALID && mLteRssnr == -1)) ? mLteRssnr : SignalStrength.INVALID; + + mTdScdmaRscp = ((mTdScdmaRscp >= 25) && (mTdScdmaRscp <= 120)) + ? -mTdScdmaRscp : SignalStrength.INVALID; + // Cqi no change if (DBG) log("Signal after validate=" + this); } @@ -479,14 +506,17 @@ public boolean needsOldRilFeature(String feature) { * @hide */ public int getLevel() { - int level; + int level = 0; if (isGsm) { boolean lteChecks = (getLteRsrp() == INVALID && getLteRsrq() == INVALID && getLteRssnr() == INVALID && getLteSignalStrenght() == 99); boolean oldRil = needsOldRilFeature("signalstrength"); level = getLteLevel(); - if ((level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN && getGsmAsuLevel() != 99 && lteChecks) || oldRil) { - level = getGsmLevel(); + if ((level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN && (getGsmAsuLevel() != 99 || getTdScdmaAsuLevel() != 255) && lteChecks) || oldRil) { + level = getTdScdmaLevel(); + if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { + level = getGsmLevel(); + } } } else { int cdmaLevel = getCdmaLevel(); @@ -512,12 +542,16 @@ public int getLevel() { * @hide */ public int getAsuLevel() { - int asuLevel; + int asuLevel = 0; if (isGsm) { boolean oldRil = needsOldRilFeature("signalstrength"); boolean lteChecks = (getLteRsrp() == INVALID && getLteRsrq() == INVALID && getLteRssnr() == INVALID && getLteSignalStrenght() == 99); - if ((getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN && getGsmAsuLevel() != 99 && lteChecks) || oldRil) { - asuLevel = getGsmAsuLevel(); + if ((getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN && (getGsmAsuLevel() != 99 || getTdScdmaAsuLevel() != 255) && lteChecks) || oldRil) { + if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { + asuLevel = getGsmAsuLevel(); + } else { + asuLevel = getTdScdmaAsuLevel(); + } } else { asuLevel = getLteAsuLevel(); } @@ -545,13 +579,17 @@ public int getAsuLevel() { * @hide */ public int getDbm() { - int dBm; + int dBm = INVALID; if(isGsm()) { boolean oldRil = needsOldRilFeature("signalstrength"); boolean lteChecks = (getLteRsrp() == INVALID && getLteRsrq() == INVALID && getLteRssnr() == INVALID && getLteSignalStrenght() == 99); - if ((getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN && getGsmAsuLevel() != 99 && lteChecks) || oldRil) { - dBm = getGsmDbm(); + if ((getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN && (getGsmAsuLevel() != 99 || getTdScdmaAsuLevel() != 255) && lteChecks) || oldRil) { + if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { + dBm = getGsmDbm(); + } else { + dBm = getTdScdmaDbm(); + } } else { dBm = getLteDbm(); } @@ -847,6 +885,54 @@ public boolean isGsm() { return this.isGsm; } + /** + * @return get TD_SCDMA dbm + * + * @hide + */ + public int getTdScdmaDbm() { + return this.mTdScdmaRscp; + } + + /** + * Get TD-SCDMA as level 0..4 + * Range : 25 to 120 + * INT_MAX: 0x7FFFFFFF denotes invalid value + * Reference: 3GPP TS 25.123, section 9.1.1.1 + * + * @hide + */ + public int getTdScdmaLevel() { + final int tdScdmaDbm = getTdScdmaDbm(); + int level; + + if ((tdScdmaDbm > -25) || (tdScdmaDbm == SignalStrength.INVALID)) + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT; + else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD; + else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE; + else if (tdScdmaDbm >= -120) level = SIGNAL_STRENGTH_POOR; + else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + + if (DBG) log("getTdScdmaLevel = " + level); + return level; + } + + /** + * Get the TD-SCDMA signal level as an asu value. + * + * @hide + */ + public int getTdScdmaAsuLevel() { + final int tdScdmaDbm = getTdScdmaDbm(); + int tdScdmaAsuLevel; + + if (tdScdmaDbm == INVALID) tdScdmaAsuLevel = 255; + else tdScdmaAsuLevel = tdScdmaDbm + 120; + if (DBG) log("TD-SCDMA Asu level: " + tdScdmaAsuLevel); + return tdScdmaAsuLevel; + } + /** * @return hash code */ @@ -859,7 +945,7 @@ public int hashCode() { + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum) + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum) + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum) - + (isGsm ? 1 : 0)); + + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)); } /** @@ -891,6 +977,7 @@ public boolean equals (Object o) { && mLteRsrq == s.mLteRsrq && mLteRssnr == s.mLteRssnr && mLteCqi == s.mLteCqi + && mTdScdmaRscp == s.mTdScdmaRscp && isGsm == s.isGsm); } @@ -912,6 +999,7 @@ public String toString() { + " " + mLteRsrq + " " + mLteRssnr + " " + mLteCqi + + " " + mTdScdmaRscp + " " + (isGsm ? "gsm|lte" : "cdma")); } @@ -934,6 +1022,7 @@ private void setFromNotifierBundle(Bundle m) { mLteRsrq = m.getInt("LteRsrq"); mLteRssnr = m.getInt("LteRssnr"); mLteCqi = m.getInt("LteCqi"); + mTdScdmaRscp = m.getInt("TdScdma"); isGsm = m.getBoolean("isGsm"); } @@ -956,6 +1045,7 @@ public void fillInNotifierBundle(Bundle m) { m.putInt("LteRsrq", mLteRsrq); m.putInt("LteRssnr", mLteRssnr); m.putInt("LteCqi", mLteCqi); + m.putInt("TdScdma", mTdScdmaRscp); m.putBoolean("isGsm", Boolean.valueOf(isGsm)); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 19e77b6d02f4f..d769a2d696fe5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +27,8 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.telephony.Rlog; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ITelephony; @@ -412,12 +417,22 @@ public static int getPhoneType(int networkMode) { case RILConstants.NETWORK_MODE_GSM_UMTS: case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA: case RILConstants.NETWORK_MODE_LTE_WCDMA: + case RILConstants.NETWORK_MODE_TD_SCDMA_ONLY: + case RILConstants.NETWORK_MODE_TD_SCDMA_WCDMA: + case RILConstants.NETWORK_MODE_TD_SCDMA_LTE: + case RILConstants.NETWORK_MODE_TD_SCDMA_GSM: + case RILConstants.NETWORK_MODE_TD_SCDMA_GSM_LTE: + case RILConstants.NETWORK_MODE_TD_SCDMA_GSM_WCDMA: + case RILConstants.NETWORK_MODE_TD_SCDMA_WCDMA_LTE: + case RILConstants.NETWORK_MODE_TD_SCDMA_GSM_WCDMA_LTE: return PhoneConstants.PHONE_TYPE_GSM; // Use CDMA Phone for the global mode including CDMA case RILConstants.NETWORK_MODE_GLOBAL: case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO: case RILConstants.NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA: + case RILConstants.NETWORK_MODE_TD_SCDMA_CDMA_EVDO_GSM_WCDMA: + case RILConstants.NETWORK_MODE_TD_SCDMA_LTE_CDMA_EVDO_GSM_WCDMA: return PhoneConstants.PHONE_TYPE_CDMA; case RILConstants.NETWORK_MODE_LTE_ONLY: @@ -531,7 +546,8 @@ public static int getLteOnGsmModeStatic() { * on a CDMA network). */ public String getNetworkOperatorName() { - return SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ALPHA); + return getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, + getDefaultSubscription(), ""); } /** @@ -542,7 +558,8 @@ public String getNetworkOperatorName() { * on a CDMA network). */ public String getNetworkOperator() { - return SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC); + return getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, + getDefaultSubscription(), ""); } /** @@ -552,7 +569,8 @@ public String getNetworkOperator() { * Availability: Only when user registered to a network. */ public boolean isNetworkRoaming() { - return "true".equals(SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING)); + return "true".equals(getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, + getDefaultSubscription(), "false")); } /** @@ -564,7 +582,35 @@ public boolean isNetworkRoaming() { * on a CDMA network). */ public String getNetworkCountryIso() { - return SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY); + return getTelephonyProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, + getDefaultSubscription(), ""); + } + + /** + * Gets the telephony Default Subscription. + * + * @hide + */ + public static int getDefaultSubscription() { + return SystemProperties.getInt(TelephonyProperties.PROPERTY_DEFAULT_SUBSCRIPTION, 0); + } + + + /** + * Gets the telephony property. + * + * @hide + */ + public static String getTelephonyProperty(String property, int index, String defaultVal) { + String propVal = null; + String prop = SystemProperties.get(property); + if ((prop != null) && (prop.length() > 0)) { + String values[] = prop.split(","); + if ((index >= 0) && (index < values.length) && (values[index] != null)) { + propVal = values[index]; + } + } + return propVal == null ? defaultVal : propVal; } /** Network type is unknown */ @@ -599,6 +645,10 @@ public String getNetworkCountryIso() { public static final int NETWORK_TYPE_EHRPD = 14; /** Current network is HSPA+ */ public static final int NETWORK_TYPE_HSPAP = 15; + /** Current network is GSM {@hide} */ + public static final int NETWORK_TYPE_GSM = 16; + /** Current network is TD_SCDMA {@hide} */ + public static final int NETWORK_TYPE_TD_SCDMA = 17; /** Current network is DC-HSPAP * @hide */ @@ -632,6 +682,7 @@ public int getNetworkType() { * @see #NETWORK_TYPE_LTE * @see #NETWORK_TYPE_EHRPD * @see #NETWORK_TYPE_HSPAP + * @see #NETWORK_TYPE_TD_SCDMA * @see #NETWORK_TYPE_DCHSPAP * * @hide @@ -706,6 +757,7 @@ public void toggleLTE(boolean on) { public static int getNetworkClass(int networkType) { switch (networkType) { case NETWORK_TYPE_GPRS: + case NETWORK_TYPE_GSM: case NETWORK_TYPE_EDGE: case NETWORK_TYPE_CDMA: case NETWORK_TYPE_1xRTT: @@ -721,6 +773,7 @@ public static int getNetworkClass(int networkType) { case NETWORK_TYPE_EHRPD: case NETWORK_TYPE_HSPAP: case NETWORK_TYPE_DCHSPAP: + case NETWORK_TYPE_TD_SCDMA: return NETWORK_CLASS_3_G; case NETWORK_TYPE_LTE: return NETWORK_CLASS_4_G; @@ -775,6 +828,10 @@ public static String getNetworkTypeName(int type) { return "HSPA+"; case NETWORK_TYPE_DCHSPAP: return "DCHSPAP"; + case NETWORK_TYPE_GSM: + return "GSM"; + case NETWORK_TYPE_TD_SCDMA: + return "TD_SCDMA"; default: return "UNKNOWN"; } @@ -801,6 +858,10 @@ public static String getNetworkTypeName(int type) { public static final int SIM_STATE_NETWORK_LOCKED = 4; /** SIM card state: Ready */ public static final int SIM_STATE_READY = 5; + /** SIM card state: SIM Card Error, Sim Card is present but faulty + *@hide + */ + public static final int SIM_STATE_CARD_IO_ERROR = 6; /** * @return true if a ICC card is present @@ -827,9 +888,11 @@ public boolean hasIccCard() { * @see #SIM_STATE_PUK_REQUIRED * @see #SIM_STATE_NETWORK_LOCKED * @see #SIM_STATE_READY + * @see #SIM_STATE_CARD_IO_ERROR */ public int getSimState() { - String prop = SystemProperties.get(TelephonyProperties.PROPERTY_SIM_STATE); + String prop = getTelephonyProperty(TelephonyProperties.PROPERTY_SIM_STATE, + getDefaultSubscription(), ""); if ("ABSENT".equals(prop)) { return SIM_STATE_ABSENT; } @@ -839,12 +902,15 @@ else if ("PIN_REQUIRED".equals(prop)) { else if ("PUK_REQUIRED".equals(prop)) { return SIM_STATE_PUK_REQUIRED; } - else if ("NETWORK_LOCKED".equals(prop)) { + else if ("PERSO_LOCKED".equals(prop)) { return SIM_STATE_NETWORK_LOCKED; } else if ("READY".equals(prop)) { return SIM_STATE_READY; } + else if ("CARD_IO_ERROR".equals(prop)) { + return SIM_STATE_CARD_IO_ERROR; + } else { return SIM_STATE_UNKNOWN; } @@ -859,7 +925,8 @@ else if ("READY".equals(prop)) { * @see #getSimState */ public String getSimOperator() { - return SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC); + return getTelephonyProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, + getDefaultSubscription(), ""); } /** @@ -870,14 +937,16 @@ public String getSimOperator() { * @see #getSimState */ public String getSimOperatorName() { - return SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA); + return getTelephonyProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, + getDefaultSubscription(), ""); } /** * Returns the ISO country code equivalent for the SIM provider's country code. */ public String getSimCountryIso() { - return SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY); + return getTelephonyProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, + getDefaultSubscription(), ""); } /** diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java index 313bc82a07ffa..28360da0e73ce 100644 --- a/telephony/java/android/telephony/gsm/GsmCellLocation.java +++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java @@ -23,17 +23,14 @@ * Represents the cell location on a GSM phone. */ public class GsmCellLocation extends CellLocation { - private int mLac; - private int mCid; - private int mPsc; + private int mLac = -1; + private int mCid = -1; + private int mPsc = -1; /** - * Empty constructor. Initializes the LAC and CID to -1. + * Empty constructor. */ public GsmCellLocation() { - mLac = -1; - mCid = -1; - mPsc = -1; } /** diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index 228a630e40d8f..d5423fd154e0e 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -21,6 +21,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.location.CountryDetector; +import android.location.Country; import android.net.Uri; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; @@ -549,6 +550,8 @@ private static String getGeoDescription(Context context, String number) { if (VDBG) Rlog.v(TAG, "- parsed number: " + pn); } catch (NumberParseException e) { Rlog.w(TAG, "getGeoDescription: NumberParseException for incoming number '" + number + "'"); + } catch (Exception e) { + Log.w(TAG, "Exception Caught" + e); } if (pn != null) { @@ -566,14 +569,17 @@ private static String getGeoDescription(Context context, String number) { */ private static String getCurrentCountryIso(Context context, Locale locale) { String countryIso; + Country country; CountryDetector detector = (CountryDetector) context.getSystemService( Context.COUNTRY_DETECTOR); - if (detector != null) { - countryIso = detector.detectCountry().getCountryIso(); + if ((detector != null) && ((country = detector.detectCountry()) != null)) { + countryIso = country.getCountryIso(); + } else if (locale != null) { + countryIso = locale.getCountry(); + Rlog.w(TAG, "No CountryDetector; falling back to countryIso based on locale: " + + countryIso); } else { - countryIso = locale.getCountry(); - Rlog.w(TAG, "No CountryDetector; falling back to countryIso based on locale: " - + countryIso); + countryIso = "US"; //default value is "US" } return countryIso; } diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index dd5f6444c3d79..bf1cf4a060091 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -21,6 +21,7 @@ import android.database.Cursor; import android.database.SQLException; import android.location.CountryDetector; +import android.location.Country; import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -271,11 +272,17 @@ protected void onQueryComplete(int token, Object cookie, Cursor cursor) { // Use the number entered by the user for display. if (!TextUtils.isEmpty(cw.number)) { + Country country; CountryDetector detector = (CountryDetector) mQueryContext.getSystemService( Context.COUNTRY_DETECTOR); - mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number, - mCallerInfo.normalizedNumber, - detector.detectCountry().getCountryIso()); + if (detector != null) { + country = detector.detectCountry(); + if (country != null) { + mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number, + mCallerInfo.normalizedNumber, + country.getCountryIso()); + } + } } } diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index eb93e9c1c027c..892864043d5dd 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -93,9 +96,12 @@ public enum Activity { public static final int EVENT_ICC_CHANGED = BASE + 33; public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34; public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35; - public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36; - public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37; - public static final int CMD_IS_PROVISIONING_APN = BASE + 38; + public static final int EVENT_TETHERED_MODE_STATE_CHANGED = BASE + 36; + public static final int EVENT_MODEM_DATA_PROFILE_READY= BASE + 37; + public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 38; + public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 39; + public static final int CMD_IS_PROVISIONING_APN = BASE + 40; + public static final int EVENT_DATA_RAT_CHANGED = BASE + 41; /***** Constants *****/ diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index 94da18ca850a2..8d18287fda3b8 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -1,5 +1,6 @@ /* ** Copyright 2007, The Android Open Source Project +** Copyright (c) 2012, The Linux Foundation. All rights reserved. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -119,6 +120,35 @@ interface ISms { void sendText(String callingPkg, in String destAddr, in String scAddr, in String text, in PendingIntent sentIntent, in PendingIntent deliveryIntent); + /** + * Send an SMS. + * + * @param destAddr the address to send the message to + * @param smsc the SMSC to send the message through, or NULL for the + * default SMSC + * @param text the body of the message to send + * @param sentIntent if not NULL this PendingIntent is + * broadcast when the message is sucessfully sent, or failed. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors:
    + * RESULT_ERROR_GENERIC_FAILURE
    + * RESULT_ERROR_RADIO_OFF
    + * RESULT_ERROR_NULL_PDU
    + * For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include + * the extra "errorCode" containing a radio technology specific value, + * generally only useful for troubleshooting.
    + * The per-application based SMS control checks sentIntent. If sentIntent + * is NULL the caller will be checked against all unknown applications, + * which cause smaller number of SMS to be sent in checking period. + * @param deliveryIntent if not NULL this PendingIntent is + * broadcast when the message is delivered to the recipient. The + * raw pdu of the status report is in the extended data ("pdu"). + * @param priority Priority level of the message + */ + void sendTextWithPriority(String callingPkg, in String destAddr, in String scAddr, in String text, + in PendingIntent sentIntent, in PendingIntent deliveryIntent, + in int priority); + /** * Send a multi-part text based SMS. * @@ -216,4 +246,26 @@ interface ISms { * Requires system permission. */ void setPremiumSmsPermission(String packageName, int permission); + + /** + * SMS over IMS is supported if IMS is registered and SMS is supported + * on IMS. + * + * @return true if SMS over IMS is supported, false otherwise + * + * @see #getImsSmsFormat() + */ + boolean isImsSmsSupported(); + + /** + * Gets SMS format supported on IMS. SMS over IMS format is + * either 3GPP or 3GPP2. + * + * @return android.telephony.SmsMessage.FORMAT_3GPP, + * android.telephony.SmsMessage.FORMAT_3GPP2 + * or android.telephony.SmsMessage.FORMAT_UNKNOWN + * + * @see #isImsSmsSupported() + */ + String getImsSmsFormat(); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index db33de03a4508..091fac6418af5 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -151,6 +151,29 @@ interface ITelephony { */ boolean supplyPin(String pin); + /** + * Supply a pin to unlock the SIM. Blocks until a result is determined. + * Returns a specific success/error code. + * @param pin The pin to check. + * @return Phone.PIN_RESULT_SUCCESS on success. Otherwise error code + */ + int supplyPinReportResult(String pin); + + /** + * Supply puk to unlock the SIM and set SIM pin to new pin. + * Blocks until a result is determined. + * Returns a specific success/error code. + * @param puk The puk to check + * pin The pin to check. + * @return Phone.PIN_RESULT_SUCCESS on success. Otherwise error code + */ + int supplyPukReportResult(String puk, String pin); + + /** + * Gets the number of attempts remaining for PIN1/PUK1 unlock. + */ + int getIccPin1RetryCount(); + /** * Supply puk to unlock the SIM and set SIM pin to new pin. * Blocks until a result is determined. diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistryMSim.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistryMSim.aidl new file mode 100644 index 0000000000000..c8bbdecf2f638 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistryMSim.aidl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * 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.telephony; + +import android.content.Intent; +import android.net.LinkProperties; +import android.net.LinkCapabilities; +import android.os.Bundle; +import android.telephony.CellInfo; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import com.android.internal.telephony.IPhoneStateListener; + +interface ITelephonyRegistryMSim { + void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow, + int subscription); + + void notifyCallState(int state, String incomingNumber, in int subscription); + void notifyServiceState(in ServiceState state, in int subscription); + void notifySignalStrength(in SignalStrength signalStrength, in int subscription); + void notifyMessageWaitingChanged(boolean mwi, in int subscription); + void notifyCallForwardingChanged(boolean cfi, in int subscription); + void notifyDataActivity(int state); + void notifyDataConnection(int state, boolean isDataConnectivityPossible, + String reason, String apn, String apnType, in LinkProperties linkProperties, + in LinkCapabilities linkCapabilities, int networkType, boolean roaming); + void notifyDataConnectionFailed(String reason, String apnType); + void notifyCellLocation(in Bundle cellLocation, in int subscription); + void notifyOtaspChanged(in int otaspMode); + void notifyCellInfo(in List cellInfo, in int subscription); +} diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java index 236bb2fb5a571..29b2f193c2d8f 100644 --- a/telephony/java/com/android/internal/telephony/IccCardConstants.java +++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java @@ -28,6 +28,8 @@ public class IccCardConstants { public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY"; /* ABSENT means ICC is missing */ public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT"; + /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */ + static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR"; /* LOCKED means ICC is locked by pin or by network */ public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED"; /* READY means ICC is ready to access */ @@ -42,8 +44,8 @@ public class IccCardConstants { public static final String INTENT_VALUE_LOCKED_ON_PIN = "PIN"; /* PUK means ICC is locked on PUK1 */ public static final String INTENT_VALUE_LOCKED_ON_PUK = "PUK"; - /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */ - public static final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK"; + /* PERSO means ICC is locked on PERSONALIZATION */ + public static final String INTENT_VALUE_LOCKED_PERSO = "PERSO"; /* PERM_DISABLED means ICC is permanently disabled due to puk fails */ public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED"; @@ -60,10 +62,12 @@ public enum State { ABSENT, PIN_REQUIRED, PUK_REQUIRED, - NETWORK_LOCKED, + PERSO_LOCKED, READY, NOT_READY, - PERM_DISABLED; + PERM_DISABLED, + CARD_IO_ERROR, + DETECTED; public boolean isPinLocked() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); @@ -71,8 +75,9 @@ public boolean isPinLocked() { public boolean iccCardExist() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED) - || (this == NETWORK_LOCKED) || (this == READY) - || (this == PERM_DISABLED)); + || (this == PERSO_LOCKED) || (this == READY) + || (this == PERM_DISABLED) || (this == CARD_IO_ERROR) + || (this == DETECTED)); } } } diff --git a/telephony/java/com/android/internal/telephony/MSimConstants.java b/telephony/java/com/android/internal/telephony/MSimConstants.java new file mode 100644 index 0000000000000..9d35fd3538738 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/MSimConstants.java @@ -0,0 +1,62 @@ +/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.internal.telephony; + +public class MSimConstants { + + public static final int DEFAULT_SUBSCRIPTION = 0; + + public static final int INVALID_SUBSCRIPTION = -1; + + public static final int RIL_CARD_MAX_APPS = 8; + + public static final int DEFAULT_CARD_INDEX = 0; + + public static final int MAX_PHONE_COUNT_SINGLE_SIM = 1; + + public static final int MAX_PHONE_COUNT_DUAL_SIM = 2; + + public static final int MAX_PHONE_COUNT_TRI_SIM = 3; + + public static final String SUBSCRIPTION_KEY = "subscription"; + + public static final int SUB1 = 0; + public static final int SUB2 = 1; + public static final int SUB3 = 2; + + public static final int EVENT_SUBSCRIPTION_ACTIVATED = 500; + public static final int EVENT_SUBSCRIPTION_DEACTIVATED = 501; + + public enum CardUnavailableReason { + REASON_CARD_REMOVED, + REASON_RADIO_UNAVAILABLE, + REASON_SIM_REFRESH_RESET + }; +} diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index 16ea625cce946..a81da2f938cd1 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -57,6 +57,7 @@ public enum DataState { public static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE; public static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE; public static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE; + public static final int PHONE_TYPE_IMS = RILConstants.IMS_PHONE; // Modes for LTE_ON_CDMA public static final int LTE_ON_CDMA_UNKNOWN = RILConstants.LTE_ON_CDMA_UNKNOWN; @@ -85,6 +86,14 @@ public enum DataState { public static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged"; + /** + * Return codes for supplyPinReturnResult and + * supplyPukReturnResult APIs + */ + public static final int PIN_RESULT_SUCCESS = 0; + public static final int PIN_PASSWORD_INCORRECT = 1; + public static final int PIN_GENERAL_FAILURE = 2; + /** * Return codes for enableApnType() */ diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 194d9fcc00d5f..cd453aa38f0ba 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + *Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,6 +58,16 @@ class C */ int ILLEGAL_SIM_OR_ME = 15; /* network selection failure due to wrong SIM/ME and no retries needed */ + int DIAL_MODIFIED_TO_USSD = 17; /* DIAL request modified to USSD */ + int DIAL_MODIFIED_TO_SS = 18; /* DIAL request modified to SS */ + int DIAL_MODIFIED_TO_DIAL = 19; /* DIAL request modified to DIAL with different data */ + int USSD_MODIFIED_TO_DIAL = 20; /* USSD request modified to DIAL */ + int USSD_MODIFIED_TO_SS = 21; /* USSD request modified to SS */ + int USSD_MODIFIED_TO_USSD = 22; /* USSD request modified to different USSD request */ + int SS_MODIFIED_TO_DIAL = 23; /* SS request modified to DIAL */ + int SS_MODIFIED_TO_USSD = 24; /* SS request modified to USSD */ + int SS_MODIFIED_TO_SS = 25; /* SS request modified to different SS request */ + int SUBSCRIPTION_NOT_SUPPORTED = 26; /* Subscription not supported */ /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */ int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */ @@ -73,6 +86,17 @@ class C */ int NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */ int NETWORK_MODE_LTE_ONLY = 11; /* LTE Only mode. */ int NETWORK_MODE_LTE_WCDMA = 12; /* LTE/WCDMA */ + int NETWORK_MODE_TD_SCDMA_ONLY = 13; /* TD-SCDMA only */ + int NETWORK_MODE_TD_SCDMA_WCDMA = 14; /* TD-SCDMA and WCDMA */ + int NETWORK_MODE_TD_SCDMA_LTE = 15; /* TD-SCDMA and LTE */ + int NETWORK_MODE_TD_SCDMA_GSM = 16; /* TD-SCDMA and GSM */ + int NETWORK_MODE_TD_SCDMA_GSM_LTE = 17; /* TD-SCDMA,GSM and LTE */ + int NETWORK_MODE_TD_SCDMA_GSM_WCDMA = 18; /* TD-SCDMA, GSM/WCDMA */ + int NETWORK_MODE_TD_SCDMA_WCDMA_LTE = 19; /* TD-SCDMA, WCDMA and LTE */ + int NETWORK_MODE_TD_SCDMA_GSM_WCDMA_LTE = 20; /* TD-SCDMA, GSM/WCDMA and LTE */ + int NETWORK_MODE_TD_SCDMA_CDMA_EVDO_GSM_WCDMA = 21; /*TD-SCDMA,EvDo,CDMA,GSM/WCDMA*/ + int NETWORK_MODE_TD_SCDMA_LTE_CDMA_EVDO_GSM_WCDMA = 22; /* TD-SCDMA/LTE/GSM/WCDMA, CDMA, and + EvDo */ int PREFERRED_NETWORK_MODE = NETWORK_MODE_WCDMA_PREF; int CDMA_CELL_BROADCAST_SMS_DISABLED = 1; @@ -82,6 +106,7 @@ class C */ int GSM_PHONE = 1; int CDMA_PHONE = 2; int SIP_PHONE = 3; + int IMS_PHONE = 4; int LTE_ON_CDMA_UNKNOWN = -1; int LTE_ON_CDMA_FALSE = 0; @@ -152,6 +177,12 @@ class C */ public static final int DATA_PROFILE_CBS = 4; public static final int DATA_PROFILE_OEM_BASE = 1000; + /** + * Tethered mode on/off indication for RIL_UNSOL_TETHERED_MODE_STATE_CHANGED + */ + int RIL_TETHERED_MODE_ON = 1; + int RIL_TETHERED_MODE_OFF = 0; + int RIL_REQUEST_GET_SIM_STATUS = 1; int RIL_REQUEST_ENTER_SIM_PIN = 2; int RIL_REQUEST_ENTER_SIM_PUK = 3; @@ -159,7 +190,7 @@ class C */ int RIL_REQUEST_ENTER_SIM_PUK2 = 5; int RIL_REQUEST_CHANGE_SIM_PIN = 6; int RIL_REQUEST_CHANGE_SIM_PIN2 = 7; - int RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION = 8; + int RIL_REQUEST_ENTER_DEPERSONALIZATION_CODE = 8; int RIL_REQUEST_GET_CURRENT_CALLS = 9; int RIL_REQUEST_DIAL = 10; int RIL_REQUEST_GET_IMSI = 11; @@ -262,6 +293,13 @@ class C */ int RIL_REQUEST_VOICE_RADIO_TECH = 108; int RIL_REQUEST_GET_CELL_INFO_LIST = 109; int RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE = 110; + int RIL_REQUEST_IMS_REGISTRATION_STATE = 111; + int RIL_REQUEST_IMS_SEND_SMS = 112; + int RIL_REQUEST_GET_DATA_CALL_PROFILE = 113; + int RIL_REQUEST_SET_UICC_SUBSCRIPTION = 114; + int RIL_REQUEST_SET_DATA_SUBSCRIPTION = 115; + int RIL_REQUEST_GET_UICC_SUBSCRIPTION = 116; + int RIL_REQUEST_GET_DATA_SUBSCRIPTION = 117; int RIL_UNSOL_RESPONSE_BASE = 1000; int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000; int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001; @@ -300,5 +338,11 @@ class C */ int RIL_UNSOL_RIL_CONNECTED = 1034; int RIL_UNSOL_VOICE_RADIO_TECH_CHANGED = 1035; int RIL_UNSOL_CELL_INFO_LIST = 1036; + int RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED = 1037; + int RIL_UNSOL_TETHERED_MODE_STATE_CHANGED = 1038; + int RIL_UNSOL_ON_SS = 1039; + int RIL_UNSOL_STK_CC_ALPHA_NOTIFY = 1040; + int RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED = 1041; + int RIL_UNSOL_STK_SEND_SMS_RESULT = 11002; /* Samsung STK */ } diff --git a/telephony/java/com/android/internal/telephony/SmsConstants.java b/telephony/java/com/android/internal/telephony/SmsConstants.java index 1ccdc3b24eefb..a36697a1c4957 100644 --- a/telephony/java/com/android/internal/telephony/SmsConstants.java +++ b/telephony/java/com/android/internal/telephony/SmsConstants.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (c) 2012, The Linux Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +62,12 @@ public enum MessageClass{ UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3; } + /** + * Indicates unknown format SMS message. + * @hide pending API council approval + */ + public static final String FORMAT_UNKNOWN = "unknown"; + /** * Indicates a 3GPP format SMS message. * @hide pending API council approval diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 40a3c8fceb357..084798971e100 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -298,4 +298,16 @@ public class TelephonyIntents { public static final String EXTRA_PLMN = "plmn"; public static final String EXTRA_SHOW_SPN = "showSpn"; public static final String EXTRA_SPN = "spn"; + + /** + * Managed Roaming Intent. Used by Phone App to show popup to the end user that location update + * request rejected with status as "Persistent location update reject", so user can try to do + * location update on other Network:

    + * + *

    This is a protected intent that can only be sent + * by the system. + * @hide + */ + public static final String ACTION_MANAGED_ROAMING_IND + = "qualcomm.intent.action.ACTION_MANAGED_ROAMING_IND"; } diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java old mode 100644 new mode 100755 index 3427bf63ae295..e7c3ad6360b9c --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -1,5 +1,8 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -98,7 +101,7 @@ public interface TelephonyProperties //****** SIM Card /** * One of "UNKNOWN" "ABSENT" "PIN_REQUIRED" - * "PUK_REQUIRED" "NETWORK_LOCKED" or "READY" + * "PUK_REQUIRED" "PERSO_LOCKED" or "READY" */ static String PROPERTY_SIM_STATE = "gsm.sim.state"; @@ -108,6 +111,12 @@ public interface TelephonyProperties */ static String PROPERTY_ICC_OPERATOR_NUMERIC = "gsm.sim.operator.numeric"; + /** The MCC+MNC (mobile country code+mobile network code) of the + * provider of the SIM to be used for APNs lookup. 5 or 6 decimal digits. + * Availability: SIM state must be "READY" + */ + static String PROPERTY_APN_SIM_OPERATOR_NUMERIC = "gsm.apn.sim.operator.numeric"; + /** PROPERTY_ICC_OPERATOR_ALPHA is also known as the SPN, or Service Provider Name. * Availability: SIM state must be "READY" */ @@ -188,6 +197,17 @@ public interface TelephonyProperties */ static final String PROPERTY_TEST_CSIM = "persist.radio.test-csim"; + /** + * Specify if Android supports VoLTE/VT calls on IMS + */ + static final String CALLS_ON_IMS_ENABLED_PROPERTY = "persist.radio.calls.on.ims"; + + /** + * Specify if Android supports CSVT calls. + */ + static final String PROPERTY_CSVT_ENABLED = "persist.radio.csvt.enabled"; + + /** * Ignore RIL_UNSOL_NITZ_TIME_RECEIVED completely, used for debugging/testing. */ @@ -203,4 +223,21 @@ public interface TelephonyProperties * Type: boolean ( true = alpha display enabled, false = alpha display disabled) */ static final String PROPERTY_ALPHA_USRCNF = "persist.atel.noalpha.usrcnf"; + + /** + * Property to set multi sim feature. + * Type: String(dsds, dsda) + */ + static final String PROPERTY_MULTI_SIM_CONFIG = "persist.multisim.config"; + + /** + * Property to store default subscription. + */ + static final String PROPERTY_DEFAULT_SUBSCRIPTION = "persist.radio.default.sub"; + + /** + * Property to enable MMS Mode. + * Type: string ( default = silent, enable to = prompt ) + */ + static final String PROPERTY_MMS_TRANSACTION = "mms.transaction"; } diff --git a/telephony/java/com/android/internal/telephony/msim/IPhoneSubInfoMSim.aidl b/telephony/java/com/android/internal/telephony/msim/IPhoneSubInfoMSim.aidl new file mode 100644 index 0000000000000..693a4630df958 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/msim/IPhoneSubInfoMSim.aidl @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2011-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * 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.telephony.msim; + +/** + * Interface used to retrieve various phone-related subscriber information. + * {@hide} + */ +interface IPhoneSubInfoMSim { + + /** + * Retrieves the unique device ID of a subscription for the device, e.g., IMEI + * for GSM phones. + */ + String getDeviceId(int subscription); + + /** + * Retrieves the software version number of a subscription for the device, e.g., IMEI/SV + * for GSM phones. + */ + String getDeviceSvn(int subscription); + + /** + * Retrieves the unique subscriber ID of a given subscription, e.g., IMSI for GSM phones. + */ + String getSubscriberId(int subscription); + + /** + * Retrieves the serial number of a given subscription. + */ + String getIccSerialNumber(int subscription); + + /** + * Retrieves the phone number string for line 1 of a subcription. + */ + String getLine1Number(int subscription); + + /** + * Retrieves the alpha identifier for line 1 of a subscription. + */ + String getLine1AlphaTag(int subscription); + + /** + * Retrieves the Msisdn of a subscription. + */ + String getMsisdn(int subscription); + + /** + * Retrieves the voice mail number of a given subscription. + */ + String getVoiceMailNumber(int subscription); + + /** + * Retrieves the complete voice mail number for particular subscription + */ + String getCompleteVoiceMailNumber(int subscription); + + /** + * Retrieves the alpha identifier associated with the voice mail number + * of a subscription. + */ + String getVoiceMailAlphaTag(int subscription); + +} diff --git a/telephony/java/com/android/internal/telephony/msim/ITelephonyMSim.aidl b/telephony/java/com/android/internal/telephony/msim/ITelephonyMSim.aidl new file mode 100644 index 0000000000000..763c44a6eb7b9 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/msim/ITelephonyMSim.aidl @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2011-2013 The Linux Foundation. All rights reserved. + * + * Not a Contribution. + * + * 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.telephony.msim; + +import android.os.Bundle; +import java.util.List; +import android.telephony.NeighboringCellInfo; +import android.telephony.CellInfo; + +/** + * Interface used to interact with the phone. Mostly this is used by the + * TelephonyManager class. A few places are still using this directly. + * Please clean them up if possible and use TelephonyManager instead. + * + * {@hide} + */ +interface ITelephonyMSim { + /** + * Dial a number. This doesn't place the call. It displays + * the Dialer screen for that subscription. + * @param number the number to be dialed. If null, this + * would display the Dialer screen with no number pre-filled. + * @param subscription user preferred subscription. + */ + void dial(String number, int subscription); + + /** + * Place a call to the specified number on particular subscription. + * @param number the number to be called. + * @param subscription user preferred subscription. + */ + void call(String number, int subscription); + + /** + * If there is currently a call in progress, show the call screen. + * The DTMF dialpad may or may not be visible initially, depending on + * whether it was up when the user last exited the InCallScreen. + * + * @return true if the call screen was shown. + */ + boolean showCallScreen(); + + /** + * Variation of showCallScreen() that also specifies whether the + * DTMF dialpad should be initially visible when the InCallScreen + * comes up. + * + * @param showDialpad if true, make the dialpad visible initially, + * otherwise hide the dialpad initially. + * @return true if the call screen was shown. + * + * @see showCallScreen + */ + boolean showCallScreenWithDialpad(boolean showDialpad); + + /** + * End call on particular subscription or go to the Home screen + * @param subscription user preferred subscription. + * @return whether it hung up + */ + boolean endCall(int subscription); + + /** + * Answer the currently-ringing call on particular subscription. + * + * If there's already a current active call, that call will be + * automatically put on hold. If both lines are currently in use, the + * current active call will be ended. + * + * TODO: provide a flag to let the caller specify what policy to use + * if both lines are in use. (The current behavior is hardwired to + * "answer incoming, end ongoing", which is how the CALL button + * is specced to behave.) + * + * TODO: this should be a oneway call (especially since it's called + * directly from the key queue thread). + * + * @param subscription user preferred subscription. + */ + void answerRingingCall(int subscription); + + /** + * Silence the ringer if an incoming call is currently ringing. + * (If vibrating, stop the vibrator also.) + * + * It's safe to call this if the ringer has already been silenced, or + * even if there's no incoming call. (If so, this method will do nothing.) + * + * TODO: this should be a oneway call too (see above). + * (Actually *all* the methods here that return void can + * probably be oneway.) + */ + void silenceRinger(); + + /** + * Check if a particular subscription has an active or holding call + * + * @param subscription user preferred subscription. + * @return true if the phone state is OFFHOOK. + */ + boolean isOffhook(int subscription); + + /** + * Check if an incoming phone call is ringing or call waiting + * on a particular subscription. + * + * @param subscription user preferred subscription. + * @return true if the phone state is RINGING. + */ + boolean isRinging(int subscription); + + /** + * Check if the phone is idle on a particular subscription. + * + * @param subscription user preferred subscription. + * @return true if the phone state is IDLE. + */ + boolean isIdle(int subscription); + + /** + * Check to see if the radio is on or not on particular subscription. + * @param subscription user preferred subscription. + * @return returns true if the radio is on. + */ + boolean isRadioOn(int subscription); + + /** + * Check if the SIM pin lock is enable + * for particular subscription. + * @param subscription user preferred subscription. + * @return true if the SIM pin lock is enabled. + */ + boolean isSimPinEnabled(int subscription); + + /** + * Cancels the missed calls notification on particular subscription. + * @param subscription user preferred subscription. + */ + void cancelMissedCallsNotification(int subscription); + + /** + * Supply a pin to unlock the SIM for particular subscription. + * Blocks until a result is determined. + * @param pin The pin to check. + * @param subscription user preferred subscription. + * @return whether the operation was a success. + */ + boolean supplyPin(String pin, int subscription); + + /** + * Supply puk to unlock the SIM and set SIM pin to new pin. + * Blocks until a result is determined. + * @param puk The puk to check. + * pin The new pin to be set in SIM + * @param subscription user preferred subscription. + * @return whether the operation was a success. + */ + boolean supplyPuk(String puk, String pin, int subscription); + + /** + * Gets the number of attempts remaining for PIN1/PUK1 unlock. + * @param subscription for which attempts remaining is required. + */ + int getIccPin1RetryCount(int subscription); + + /** + * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated + * without SEND (so dial is not appropriate) for + * a particular subscription. + * @param dialString the MMI command to be executed. + * @param subscription user preferred subscription. + * @return true if MMI command is executed. + */ + boolean handlePinMmi(String dialString, int subscription); + + /** + * Toggles the radio on or off on particular subscription. + * @param subscription user preferred subscription. + */ + void toggleRadioOnOff(int subscription); + + /** + * Set the radio to on or off on particular subscription. + * @param subscription user preferred subscription. + */ + boolean setRadio(boolean turnOn, int subscription); + + /** + * Request to update location information for a subscrition in service state + * @param subscription user preferred subscription. + */ + void updateServiceLocation(int subscription); + + /** + * Enable a specific APN type. + */ + int enableApnType(String type); + + /** + * Disable a specific APN type. + */ + int disableApnType(String type); + + /** + * Allow mobile data connections. + */ + boolean enableDataConnectivity(); + + /** + * Disallow mobile data connections. + */ + boolean disableDataConnectivity(); + + /** + * Report whether data connectivity is possible. + */ + boolean isDataConnectivityPossible(); + + /** + * Returns the call state for a subscription. + */ + int getCallState(int subscription); + int getDataActivity(); + int getDataState(); + + /** + * Returns the current active phone type as integer for particular subscription. + * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE + * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE + * @param subscription user preferred subscription. + */ + int getActivePhoneType(int subscription); + + + /** + * Returns the CDMA ERI icon index to display on particular subscription. + * @param subscription user preferred subscription. + */ + int getCdmaEriIconIndex(int subscription); + + /** + * Returns the CDMA ERI icon mode on particular subscription, + * 0 - ON + * 1 - FLASHING + * @param subscription user preferred subscription. + */ + int getCdmaEriIconMode(int subscription); + + /** + * Returns the CDMA ERI text for particular subscription, + * @param subscription user preferred subscription. + */ + String getCdmaEriText(int subscription); + + /** + * Returns true if OTA service provisioning needs to run. + * Only relevant on some technologies, others will always + * return false. + */ + boolean needsOtaServiceProvisioning(); + + /** + * Returns the unread count of voicemails for a subscription. + * @param subscription user preferred subscription. + * Returns the unread count of voicemails + */ + int getVoiceMessageCount(int subscription); + + /** + * Returns the network type of a subscription. + * @param subscription user preferred subscription. + * Returns the network type + */ + int getNetworkType(int subscription); + + /** + * Returns the data network type of a subscription + * @param subscription user preferred subscription. + * Returns the network type + */ + int getDataNetworkType(int subscription); + + /** + * Returns the voice network type of a subscription + * @param subscription user preferred subscription. + * Returns the network type + */ + int getVoiceNetworkType(int subscription); + + /** + * Return true if an ICC card is present for a subscription. + * @param subscription user preferred subscription. + * Return true if an ICC card is present + */ + boolean hasIccCard(int subscription); + + /** + * Return if the current radio is LTE on CDMA. This + * is a tri-state return value as for a period of time + * the mode may be unknown. + * + * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE} + * or {@link PHone#LTE_ON_CDMA_TRUE} + */ + int getLteOnCdmaMode(int subscription); + + /** + * Returns the all observed cell information of the device. + */ + List getAllCellInfo(); + + /** + * get default subscription + * @return subscription id + */ + int getDefaultSubscription(); + + /** + * get user prefered voice subscription + * @return subscription id + */ + int getPreferredVoiceSubscription(); + + /** + * get user prefered data subscription + * @return subscription id + */ + int getPreferredDataSubscription(); + + /* + * Set user prefered data subscription + * @return true if success + */ + boolean setPreferredDataSubscription(int subscription); +} + diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 0d07cc3264f93..56c7ffd59eb3c 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -56,7 +56,7 @@ interface IWifiManager WifiInfo getConnectionInfo(); - boolean setWifiEnabled(boolean enable); + boolean setWifiEnabled(String callingPackage, boolean enable); int getWifiEnabledState(); diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java index cef59133eabb4..87dfd0bef26e4 100644 --- a/wifi/java/android/net/wifi/WifiConfigStore.java +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -296,6 +296,15 @@ void updateStatus(int netId, DetailedState state) { */ boolean forgetNetwork(int netId) { if (mWifiNative.removeNetwork(netId)) { + for(WifiConfiguration config : mConfiguredNetworks.values()) { + if(config != null && config.status == Status.DISABLED) { + if(mWifiNative.enableNetwork(config.networkId, false)) { + config.status = Status.ENABLED; + } else { + loge("Enable network failed on " + config.networkId); + } + } + } mWifiNative.saveConfig(); removeConfigAndSendBroadcastIfNeeded(netId); return true; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b3ec7350052ef..a17120a12d1c8 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -940,7 +940,7 @@ public DhcpInfo getDhcpInfo() { */ public boolean setWifiEnabled(boolean enabled) { try { - return mService.setWifiEnabled(enabled); + return mService.setWifiEnabled(mContext.getBasePackageName(), enabled); } catch (RemoteException e) { return false; } diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index d1ea213735579..4a918a390d2ba 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -212,7 +212,7 @@ public String getMacAddress() { * RANGE=ID- gets results from ID * MASK= see wpa_supplicant/src/common/wpa_ctrl.h for details */ - public String scanResults(int sid) { + public String scanResults (int sid) { return doStringCommand("BSS RANGE=" + sid + "- MASK=0x21987"); } @@ -838,6 +838,27 @@ public List getSupportedChannels() { public native static boolean setMode(int mode); + /**Create P2P GO on the operating frequency*/ + public boolean p2pGroupAddOnSpecifiedFreq(int freq) { + return doBooleanCommand("P2P_GROUP_ADD" + " freq=" + freq); + } + + /**Set Channel preferrence eg., p2p_pref_chan=81:1,81:2,81:3,81:4,81:5,81:6*/ + public boolean setPreferredChannel(int startChannel, int endChannel) { + int i = 0; + if ((startChannel == 0) || (endChannel == 0)) return false; + StringBuffer strBuf = new StringBuffer(); + String command = "SET p2p_pref_chan "; + for (i = startChannel; i<=endChannel; i++) { + strBuf.append("81:" + i); + strBuf.append(","); + } + strBuf.deleteCharAt(strBuf.length() - 1); + command += strBuf; + Log.d(mTAG, "setPreferredChannel Command that goes to Supplicant is=" + command); + return doBooleanCommand(command) && doBooleanCommand("SAVE_CONFIG"); + } + /* Set the current mode of miracast operation. * 0 = disabled * 1 = operating as source diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 981902f7b0d15..b2c54e14c444f 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -70,6 +70,9 @@ import android.util.LruCache; import android.text.TextUtils; +import android.util.Log; +import android.text.TextUtils; +import android.util.LruCache; import com.android.internal.R; import com.android.internal.app.IBatteryStats; import com.android.internal.util.AsyncChannel; @@ -101,6 +104,7 @@ */ public class WifiStateMachine extends StateMachine { + private static final String TAG = "WifiStateMachine"; private static final String NETWORKTYPE = "WIFI"; private static final boolean DBG = false; @@ -155,6 +159,8 @@ public class WifiStateMachine extends StateMachine { private PowerManager.WakeLock mSuspendWakeLock; private List mSupportedChannels; + private int startSafeChannel = 0; + private int endSafeChannel = 0; /** * Interval in milliseconds between polling for RSSI @@ -527,6 +533,8 @@ private class TetherStateChange { private static final int DRIVER_STOP_REQUEST = 0; private static final String ACTION_DELAYED_DRIVER_STOP = "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP"; + private static final String ACTION_SAFE_WIFI_CHANNELS_CHANGED = + "qualcomm.intent.action.SAFE_WIFI_CHANNELS_CHANGED"; /** * Keep track of whether WIFI is running. @@ -612,7 +620,11 @@ public void onReceive(Context context, Intent intent) { } },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); - mContext.registerReceiver( + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_SAFE_WIFI_CHANNELS_CHANGED); + mContext.registerReceiver(WifiStateReceiver, filter); + + mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -716,6 +728,36 @@ public void onReceive(Context context, Intent intent) { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } + private BroadcastReceiver WifiStateReceiver = new BroadcastReceiver() { + + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals( + ACTION_SAFE_WIFI_CHANNELS_CHANGED)) { + startSafeChannel = intent.getIntExtra("start_safe_channel", -1); + endSafeChannel = intent.getIntExtra("end_safe_channel", -1); + Log.d(TAG, "Received WIFI_CHANNELS_CHANGED broadcast"); + int state = syncGetWifiApState(); + if (state == WIFI_AP_STATE_ENABLED) { + int autochannel = getSapAutoChannelSelection(); + Log.d(TAG,"autochannel=" + autochannel); + if (1 == autochannel){ + int currentChannel = getSapOperatingChannel(); + if (currentChannel >= 0 && + (currentChannel < startSafeChannel || + currentChannel > endSafeChannel)) { + //currently RIL passes only 2.4G channels so if the current operating + // channel is 5G channel, do not restart SAP. + if (currentChannel >= 1 && currentChannel <=14) { + Log.e(TAG, "Operating on restricted channel! Restart SAP"); + restartSoftApIfOn(); + } + } + } + } + } + } + }; + /********************************************************* * Methods exposed for public use ********************************************************/ @@ -803,6 +845,42 @@ public WifiConfiguration syncGetWifiApConfiguration() { return ret; } + /** + * Function to set Channel range. + */ + public void setChannelRange(int startchannel, int endchannel, int band) { + try { + Log.e(TAG, "setChannelRange"); + mNwService.setChannelRange(startchannel, endchannel, band); + } catch(Exception e) { + loge("Exception in setChannelRange"); + } + } + + /** + * Function to get SAP operating Channel + */ + public int getSapOperatingChannel() { + try { + return mNwService.getSapOperatingChannel(); + } catch(Exception e) { + loge("Exception in getSapOperatingChannel"); + return -1; + } + } + + /** + * Function to get Auto Channel selection + */ + public int getSapAutoChannelSelection() { + try { + return mNwService.getSapAutoChannelSelection(); + } catch (Exception e) { + loge("Exception in getSapOperatingChannel"); + return -1; + } + } + /** * TODO: doc */ @@ -1421,9 +1499,9 @@ private void setWifiApState(int wifiApState) { private static final String DELIMITER_STR = "===="; private static final String END_STR = "####"; + /** * Format: - * * id=1 * bssid=68:7f:76:d7:1a:6e * freq=2412 @@ -1464,22 +1542,22 @@ private void setScanResults() { if (lines[i].startsWith(END_STR)) { break; } else if (lines[i].startsWith(ID_STR)) { - try { - sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1; - } catch (NumberFormatException e) { - // Nothing to do - } - break; + try { + sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1; + } catch (NumberFormatException e) { + // Nothing to do + } + break; } } if (sid == -1) break; - } + } scanResults = scanResultsBuf.toString(); if (TextUtils.isEmpty(scanResults)) { + mScanResults.clear(); return; } - synchronized(mScanResultCache) { mScanResults = new ArrayList(); String[] lines = scanResults.split("\n"); @@ -1929,6 +2007,10 @@ public void run() { loge("Exception in softap start " + e); try { mNwService.stopAccessPoint(mInterfaceName); + if (startSafeChannel!=0) { + Log.e(TAG, "Calling setChannelRange ---startSoftApWithConfig()"); + setChannelRange(startSafeChannel, endSafeChannel, 0); + } mNwService.startAccessPoint(config, mInterfaceName); } catch (Exception e1) { loge("Exception in softap re-start " + e1); @@ -3623,6 +3705,10 @@ public void enter() { final WifiConfiguration config = (WifiConfiguration) message.obj; if (config == null) { + if (startSafeChannel!=0) { + Log.e(TAG, "Calling setChannelRange ---CMD_START_AP SoftApStartingState()"); + setChannelRange(startSafeChannel, endSafeChannel , 0); + } mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG); } else { mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); @@ -3860,4 +3946,13 @@ private Message obtainMessageWithArg2(Message srcMsg) { msg.arg2 = srcMsg.arg2; return msg; } + + + private void restartSoftApIfOn() { + Log.e(TAG, "Disabling wifi ap"); + setHostApRunning(null, false); + Log.e(TAG, "Enabling wifi ap"); + setHostApRunning(null, true); + Log.e(TAG, "Restart softap Done"); + } } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java index ca737f9e64d9c..76cae255ed3f2 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java @@ -60,6 +60,9 @@ public class WifiP2pGroup implements Parcelable { /** The passphrase used for WPA2-PSK */ private String mPassphrase; + /** GO operating Frequency*/ + private int mGoOperFreq; + private String mInterface; /** The network id in the wpa_supplicant */ @@ -116,6 +119,7 @@ public WifiP2pGroup(String supplicantEvent) throws IllegalArgumentException { //freq and psk are unused right now //int freq = Integer.parseInt(match.group(2)); //String psk = match.group(3); + mGoOperFreq = Integer.parseInt(match.group(2)); mPassphrase = match.group(4); mOwner = new WifiP2pDevice(match.group(5)); if (match.group(6) != null) { @@ -228,6 +232,18 @@ public Collection getClientList() { return Collections.unmodifiableCollection(mClients); } + /** @hide */ + public int setGoOperatingFrequency(int GoOperFreq) { + return mGoOperFreq = GoOperFreq; + } + + /** @hide + * This is used to fetch the GO operating frequency + */ + public int getGoOperatingFrequency() { + return mGoOperFreq; + } + /** @hide */ public void setPassphrase(String passphrase) { mPassphrase = passphrase; @@ -290,6 +306,7 @@ public WifiP2pGroup(WifiP2pGroup source) { mPassphrase = source.getPassphrase(); mInterface = source.getInterface(); mNetId = source.getNetworkId(); + mGoOperFreq = source.getGoOperatingFrequency(); } } @@ -305,6 +322,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPassphrase); dest.writeString(mInterface); dest.writeInt(mNetId); + dest.writeInt(mGoOperFreq); } /** Implement the Parcelable interface */ @@ -322,6 +340,7 @@ public WifiP2pGroup createFromParcel(Parcel in) { group.setPassphrase(in.readString()); group.setInterface(in.readString()); group.setNetworkId(in.readInt()); + group.setGoOperatingFrequency(in.readInt()); return group; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index aafa2eb0329b4..873279ad3747d 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -129,6 +129,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub { /* Idle time after a peer is gone when the group is torn down */ private static final int GROUP_IDLE_TIME_S = 10; + private static final String ACTION_SAFE_WIFI_CHANNELS_CHANGED = + "qualcomm.intent.action.SAFE_WIFI_CHANNELS_CHANGED"; private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE; @@ -185,9 +187,18 @@ public class WifiP2pService extends IWifiP2pManager.Stub { * is invoked */ private boolean mAutonomousGroup; + /**@hide*/ + public static boolean mIsWifiP2pEnabled = false; + + private int startSafeChannel = 0; + + private int endSafeChannel = 0; + /* Invitation to join an existing p2p group */ private boolean mJoinExistingGroup; + private boolean mIsInvite = false; + /* Track whether we are in p2p discovery. This is used to avoid sending duplicate * broadcasts */ @@ -314,6 +325,51 @@ public WifiP2pService(Context context) { mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported); mP2pStateMachine.start(); + // broadcasts + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_SAFE_WIFI_CHANNELS_CHANGED); + mContext.registerReceiver(new WifiStateReceiver(), filter); + } + + private class WifiStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (intent.getAction().equals(ACTION_SAFE_WIFI_CHANNELS_CHANGED)) { + Slog.d(TAG, "Received WIFI_CHANNELS_CHANGED broadcast"); + startSafeChannel = intent.getIntExtra("start_safe_channel", -1); + endSafeChannel = intent.getIntExtra("end_safe_channel", -1); + if (mIsWifiP2pEnabled) { + Slog.d(TAG, "Set Preferred channel list for Non Auto GO"); + mP2pStateMachine.mWifiNative.setPreferredChannel( + startSafeChannel, endSafeChannel); + if (mP2pStateMachine.mWifiP2pInfo.groupFormed) { + int currentChannel = 0; + currentChannel = frequencyToChannel( + mP2pStateMachine.mGroup.getGoOperatingFrequency()); + Slog.d(TAG, "GO operating frequency=" + + mP2pStateMachine.mGroup.getGoOperatingFrequency()); + Slog.d(TAG, "current channel of P2P GO=" + currentChannel); + + if (currentChannel >= 0 && + (currentChannel < startSafeChannel || + currentChannel > endSafeChannel)) { + //currently RIL passes only 2.4G channels so if the current operating + //channel is 5G channel, do not restart P2P GO. + if (currentChannel >= 1 && currentChannel <=14) { + Slog.d(TAG, "P2P GO is operating on unsafe channel! Terminate!"); + mP2pStateMachine.mWifiNative.p2pGroupRemove( + mP2pStateMachine.mGroup.getInterface()); + if (mAutonomousGroup) + mP2pStateMachine.mWifiNative.p2pGroupAddOnSpecifiedFreq( + channelToFrequency(startSafeChannel)); + } + } + + } + } + } + } } public void connectivityServiceReady() { @@ -380,6 +436,32 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(); } + /** + * Function to Convert frequency to Channel + */ + private int frequencyToChannel(int freq) { + /* see 802.11 17.3.8.3.2 and Annex J */ + if (freq == 2484) + return 14; + else if (freq < 2484) + return (freq - 2407) / 5; + else if (freq >= 4910 && freq <= 4980) + return (freq - 4000) / 5; + else + return (freq - 5000) / 5; + } + + /** + * Function to Convert Channel to frequency + */ + private int channelToFrequency(int chan) { + if (chan == 14) + return 2484; + else if (chan < 14) + return 2407 + chan * 5; + else + return 0; /* not supported */ + } /** * Handles interaction with WifiStateMachine @@ -749,6 +831,7 @@ public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { case WifiMonitor.SUP_DISCONNECTION_EVENT: + mIsWifiP2pEnabled = false; if (DBG) logd("p2p socket connection lost"); transitionTo(mP2pDisabledState); break; @@ -813,7 +896,11 @@ public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { case WifiMonitor.SUP_CONNECTION_EVENT: + if (startSafeChannel!=0) + mWifiNative.setPreferredChannel( + startSafeChannel, endSafeChannel); if (DBG) logd("P2p socket connection successful"); + mIsWifiP2pEnabled = true; transitionTo(mInactiveState); break; case WifiMonitor.SUP_DISCONNECTION_EVENT: @@ -907,8 +994,10 @@ public boolean processMessage(Message message) { break; case WifiP2pManager.DISCOVER_PEERS: if (mDiscoveryBlocked) { - replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, - WifiP2pManager.BUSY); + /* do not send discovery failure to apps. + since discovery is postponed and not failed */ + mDiscoveryPostponed = true; + logi("P2P_FIND is deffered"); break; } // do not send service discovery request while normal find operation. @@ -1093,6 +1182,7 @@ public boolean processMessage(Message message) { case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT: WifiP2pGroup group = (WifiP2pGroup) message.obj; WifiP2pDevice owner = group.getOwner(); + mIsInvite = true; if (owner == null) { loge("Ignored invitation from null owner"); @@ -1126,12 +1216,23 @@ public boolean processMessage(Message message) { break; case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: - case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: //We let the supplicant handle the provision discovery response //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT. //Handling provision discovery and issuing a p2p_connect before //group negotiation comes through causes issues break; + case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: + WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; + WifiP2pDevice device = provDisc.device; + if (device == null) { + Slog.d(TAG, "Device entry is null"); + break; + } + notifyP2pProvDiscShowPinRequest(provDisc.pin, device.deviceAddress); + mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); + sendPeersChangedBroadcast(); + transitionTo(mGroupNegotiationState); + break; case WifiP2pManager.CREATE_GROUP: mAutonomousGroup = true; int netId = message.arg1; @@ -1145,7 +1246,13 @@ public boolean processMessage(Message message) { ret = mWifiNative.p2pGroupAdd(true); } } else { - ret = mWifiNative.p2pGroupAdd(false); + Slog.d(TAG, "startChannel while creating P2P Group=" + startSafeChannel); + if (startSafeChannel!=0) { + ret = mWifiNative.p2pGroupAddOnSpecifiedFreq( + channelToFrequency(startSafeChannel)); + } else { + ret = mWifiNative.p2pGroupAdd(false); + } } if (ret) { @@ -1685,6 +1792,7 @@ public boolean processMessage(Message message) { if (DBG) logd(getName() + " remove group"); if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) { transitionTo(mOngoingGroupRemovalState); + mWifiNative.p2pFlush(); replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED); } else { handleGroupRemoved(); @@ -1787,7 +1895,6 @@ public boolean processMessage(Message message) { mSavedPeerConfig.deviceAddress, false)) { // not found the client on the list loge("Already removed the client, ignore"); - break; } // try invitation. sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig); @@ -2037,6 +2144,36 @@ private void notifyInvitationSent(String pin, String peerAddress) { dialog.show(); } + private void notifyP2pProvDiscShowPinRequest(String pin, String peerAddress) { + Resources r = Resources.getSystem(); + final String tempDevAddress = peerAddress; + final String tempPin = pin; + + final View textEntryView = LayoutInflater.from(mContext) + .inflate(R.layout.wifi_p2p_dialog, null); + + ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info); + addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress)); + addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title)) + .setView(textEntryView) + .setPositiveButton(r.getString(R.string.accept), new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mSavedPeerConfig = new WifiP2pConfig(); + mSavedPeerConfig.deviceAddress = tempDevAddress; + mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; + mSavedPeerConfig.wps.pin = tempPin; + mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); + } + }) + .setCancelable(false) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + private void notifyInvitationReceived() { Resources r = Resources.getSystem(); final WpsInfo wps = mSavedPeerConfig.wps; @@ -2217,15 +2354,24 @@ private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) { * @param config for the peer */ private void p2pConnectWithPinDisplay(WifiP2pConfig config) { + boolean join = false; WifiP2pDevice dev = fetchCurrentDeviceDetails(config); - String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner()); + if (mIsInvite) { + join = true; + } + else { + join = dev.isGroupOwner(); + } + + String pin = mWifiNative.p2pConnect(config, join); try { Integer.parseInt(pin); notifyInvitationSent(pin, config.deviceAddress); } catch (NumberFormatException ignore) { // do nothing if p2pConnect did not return a pin } + mIsInvite = false; } /**