summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt5
-rw-r--r--core/java/android/os/RemoteException.java4
-rw-r--r--core/java/android/os/TransactionTooLargeException.java59
-rw-r--r--core/java/android/webkit/FindActionModeCallback.java15
-rw-r--r--core/java/android/webkit/WebView.java19
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java17
-rw-r--r--core/jni/android_util_Binder.cpp34
-rw-r--r--core/res/AndroidManifest.xml7
-rwxr-xr-x[-rw-r--r--]core/res/res/values/config.xml5
-rwxr-xr-x[-rw-r--r--]core/res/res/values/strings.xml6
-rw-r--r--docs/html/resources/resources-data.js2
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp2
-rw-r--r--opengl/libs/EGL/egl_cache.cpp27
-rw-r--r--opengl/libs/EGL/egl_cache.h7
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.pngbin3682 -> 3693 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.pngbin3695 -> 3699 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.pngbin2183 -> 2193 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.pngbin2196 -> 2199 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.pngbin5941 -> 5948 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.pngbin5951 -> 5956 bytes
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java42
-rw-r--r--policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java54
-rw-r--r--policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/SimUnlockScreen.java2
-rw-r--r--services/audioflinger/AudioFlinger.cpp9
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java231
-rw-r--r--telephony/java/android/telephony/SmsMessage.java29
-rw-r--r--telephony/java/com/android/internal/telephony/GsmAlphabet.java8
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java111
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java62
30 files changed, 631 insertions, 128 deletions
diff --git a/api/current.txt b/api/current.txt
index d83659f..daf1036 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15132,6 +15132,7 @@ package android.os {
public class RemoteException extends android.util.AndroidException {
ctor public RemoteException();
+ ctor public RemoteException(java.lang.String);
}
public class ResultReceiver implements android.os.Parcelable {
@@ -15226,6 +15227,10 @@ package android.os {
method public abstract void released();
}
+ public class TransactionTooLargeException extends android.os.RemoteException {
+ ctor public TransactionTooLargeException();
+ }
+
public class Vibrator {
method public void cancel();
method public boolean hasVibrator();
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 9d76156..e30d24f 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -24,4 +24,8 @@ public class RemoteException extends AndroidException {
public RemoteException() {
super();
}
+
+ public RemoteException(String message) {
+ super(message);
+ }
}
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
new file mode 100644
index 0000000..25f09e8
--- /dev/null
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+import android.os.RemoteException;
+
+/**
+ * The Binder transaction failed because it was too large.
+ * <p>
+ * During a remote procedure call, the arguments and the return value of the call
+ * are transferred as {@link Parcel} objects stored in the Binder transaction buffer.
+ * If the arguments or the return value are too large to fit in the transaction buffer,
+ * then the call will fail and {@link TransactionTooLargeException} will be thrown.
+ * </p><p>
+ * The Binder transaction buffer has a limited fixed size, currently 1Mb, which
+ * is shared by all transactions in progress for the process. Consequently this
+ * exception can be thrown when there are many transactions in progress even when
+ * most of the individual transactions are of moderate size.
+ * </p><p>
+ * There are two possible outcomes when a remote procedure call throws
+ * {@link TransactionTooLargeException}. Either the client was unable to send
+ * its request to the service (most likely if the arguments were too large to fit in
+ * the transaction buffer), or the service was unable to send its response back
+ * to the client (most likely if the return value was too large to fit
+ * in the transaction buffer). It is not possible to tell which of these outcomes
+ * actually occurred. The client should assume that a partial failure occurred.
+ * </p><p>
+ * The key to avoiding {@link TransactionTooLargeException} is to keep all
+ * transactions relatively small. Try to minimize the amount of memory needed to create
+ * a {@link Parcel} for the arguments and the return value of the remote procedure call.
+ * Avoid transferring huge arrays of strings or large bitmaps.
+ * If possible, try to break up big requests into smaller pieces.
+ * </p><p>
+ * If you are implementing a service, it may help to impose size or complexity
+ * contraints on the queries that clients can perform. For example, if the result set
+ * could become large, then don't allow the client to request more than a few records
+ * at a time. Alternately, instead of returning all of the available data all at once,
+ * return the essential information first and make the client ask for additional information
+ * later as needed.
+ * </p>
+ */
+public class TransactionTooLargeException extends RemoteException {
+ public TransactionTooLargeException() {
+ super();
+ }
+}
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index b85fd17..fffa90b 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -18,6 +18,8 @@ package android.webkit;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
@@ -254,13 +256,18 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
// Does nothing. Needed to implement TextWatcher.
}
- public int getActionModeHeight() {
+ private Rect mGlobalVisibleRect = new Rect();
+ private Point mGlobalVisibleOffset = new Point();
+ public int getActionModeGlobalBottom() {
if (mActionMode == null) {
return 0;
}
- View parent = (View) mCustomView.getParent();
- return parent != null ? parent.getMeasuredHeight()
- : mCustomView.getMeasuredHeight();
+ View view = (View) mCustomView.getParent();
+ if (view == null) {
+ view = mCustomView;
+ }
+ view.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
+ return mGlobalVisibleRect.bottom;
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 55f345f..35efabc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1484,7 +1484,21 @@ public class WebView extends AbsoluteLayout
private int getVisibleTitleHeightImpl() {
// need to restrict mScrollY due to over scroll
return Math.max(getTitleHeight() - Math.max(0, mScrollY),
- mFindCallback != null ? mFindCallback.getActionModeHeight() : 0);
+ getOverlappingActionModeHeight());
+ }
+
+ private int mCachedOverlappingActionModeHeight = -1;
+
+ private int getOverlappingActionModeHeight() {
+ if (mFindCallback == null) {
+ return 0;
+ }
+ if (mCachedOverlappingActionModeHeight < 0) {
+ getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
+ mCachedOverlappingActionModeHeight = Math.max(0,
+ mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
+ }
+ return mCachedOverlappingActionModeHeight;
}
/*
@@ -3375,6 +3389,7 @@ public class WebView extends AbsoluteLayout
// Could not start the action mode, so end Find on page
return false;
}
+ mCachedOverlappingActionModeHeight = -1;
mFindCallback = callback;
setFindIsUp(true);
mFindCallback.setWebView(this);
@@ -3492,6 +3507,7 @@ public class WebView extends AbsoluteLayout
*/
void notifyFindDialogDismissed() {
mFindCallback = null;
+ mCachedOverlappingActionModeHeight = -1;
if (mWebViewCore == null) {
return;
}
@@ -4341,6 +4357,7 @@ public class WebView extends AbsoluteLayout
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ mCachedOverlappingActionModeHeight = -1;
if (mSelectingText && mOrientation != newConfig.orientation) {
selectionDone();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 17b8acf..89f9d4e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,14 +25,11 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.hardware.Camera;
-import android.hardware.Camera.CameraInfo;
import android.os.FileObserver;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.storage.IMountService;
import android.provider.Settings;
import android.security.KeyStore;
@@ -41,7 +38,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
-import android.widget.TextView;
import java.io.File;
import java.io.FileNotFoundException;
@@ -968,6 +964,11 @@ public class LockPatternUtils {
com.android.internal.R.bool.config_enable_puk_unlock_screen);
}
+ public boolean isEmergencyCallEnabledWhileSimLocked() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
+ }
+
/**
* @return A formatted string of the next alarm (for showing on the lock screen),
* or null if there is no next alarm.
@@ -1031,12 +1032,10 @@ public class LockPatternUtils {
* {@link TelephonyManager#CALL_STATE_IDLE}
* {@link TelephonyManager#CALL_STATE_RINGING}
* {@link TelephonyManager#CALL_STATE_OFFHOOK}
- * @param showIfCapable indicates whether the button should be shown if emergency calls are
- * possible on the device
+ * @param shown indicates whether the given screen wants the emergency button to show at all
*/
- public void updateEmergencyCallButtonState(Button button, int phoneState,
- boolean showIfCapable) {
- if (isEmergencyCallCapable() && showIfCapable) {
+ public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) {
+ if (isEmergencyCallCapable() && shown) {
button.setVisibility(View.VISIBLE);
} else {
button.setVisibility(View.GONE);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 1718e74..2bd4fa0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -38,6 +38,7 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/threads.h>
+#include <utils/String8.h>
#include <ScopedUtfChars.h>
#include <ScopedLocalRef.h>
@@ -651,7 +652,8 @@ jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc);
}
-void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
+static void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
+ bool canThrowRemoteException = false)
{
switch (err) {
case UNKNOWN_ERROR:
@@ -688,14 +690,25 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
break;
case DEAD_OBJECT:
- jniThrowException(env, "android/os/DeadObjectException", NULL);
+ // DeadObjectException is a checked exception, only throw from certain methods.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/DeadObjectException"
+ : "java/lang/RuntimeException", NULL);
break;
case UNKNOWN_TRANSACTION:
jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
break;
case FAILED_TRANSACTION:
LOGE("!!! FAILED BINDER TRANSACTION !!!");
- //jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large");
+ // TransactionTooLargeException is a checked exception, only throw from certain methods.
+ // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
+ // but it is not the only one. The Binder driver can return BR_FAILED_REPLY
+ // for other reasons also, such as if the transaction is malformed or
+ // refers to an FD that has been closed. We should change the driver
+ // to enable us to distinguish these cases in the future.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/TransactionTooLargeException"
+ : "java/lang/RuntimeException", NULL);
break;
case FDS_NOT_ALLOWED:
jniThrowException(env, "java/lang/RuntimeException",
@@ -703,6 +716,12 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
break;
default:
LOGE("Unknown binder error code. 0x%x", err);
+ String8 msg;
+ msg.appendFormat("Unknown binder error code. 0x%x", err);
+ // RemoteException is a checked exception, only throw from certain methods.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
+ break;
}
}
@@ -1036,8 +1055,7 @@ static bool should_time_binder_calls() {
}
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj,
- jobject replyObj, jint flags)
+ jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1084,12 +1102,12 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
return JNI_FALSE;
}
- signalExceptionForError(env, obj, err);
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
return JNI_FALSE;
}
static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
- jobject recipient, jint flags)
+ jobject recipient, jint flags) // throws RemoteException
{
if (recipient == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1114,7 +1132,7 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
// Failure adding the death recipient, so clear its reference
// now.
jdr->clearReference();
- signalExceptionForError(env, obj, err);
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
}
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0ed0523..230df39 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1027,6 +1027,13 @@
android:label="@string/permlab_readLogs"
android:description="@string/permdesc_readLogs" />
+ <!-- Allows an application to use any media decoder when decoding for playback
+ @hide -->
+ <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
+ android:protectionLevel="signatureOrSystem"
+ android:label="@string/permlab_anyCodecForPlayback"
+ android:description="@string/permdesc_anyCodecForPlayback" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8b07e34..767cafe 100644..100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -453,6 +453,11 @@
If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
<bool name="config_enable_puk_unlock_screen">true</bool>
+ <!-- Enable emergency call when sim is locked or puk locked. Some countries/carriers do not
+ allow emergency calls to be placed without the IMSI, which is locked in the SIM.
+ If so, this should be set to 'false' in an overlay. -->
+ <bool name="config_enable_emergency_call_while_sim_locked">true</bool>
+
<!-- Control the behavior when the user long presses the home button.
0 - Nothing
1 - Recent apps dialog
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2f40a5a..9b8be85 100644..100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -831,6 +831,12 @@
including personal or private information.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_anyCodecForPlayback">use any media decoder for playback</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_anyCodecForPlayback">Allows an application to use any installed
+ media decoder to decode for playback.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_diagnostic">read/write to resources owned by diag</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_diagnostic">Allows an application to read and write to
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 237e18c..41a5a51 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -578,7 +578,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'newfeature', 'performance', 'gamedev', 'gl'],
+ tags: ['sample', 'newfeature', 'performance', 'gamedev', 'gl', 'updated'],
path: 'samples/RenderScript/index.html',
title: {
en: 'RenderScript'
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index bf19040..640e9fa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -381,7 +381,7 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
}
- entry->mNotifyConsumed->setInt32("render", true);
+ entry->mNotifyConsumed->setInt32("render", !tooLate);
entry->mNotifyConsumed->post();
mVideoQueue.erase(mVideoQueue.begin());
entry = NULL;
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index aa40d58..522421b 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -34,6 +34,9 @@ static const size_t maxTotalSize = 64 * 1024;
static const char* cacheFileMagic = "EGL$";
static const size_t cacheFileHeaderSize = 8;
+// The time in seconds to wait before saving newly inserted cache entries.
+static const unsigned int deferredSaveDelay = 4;
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -128,6 +131,30 @@ void egl_cache_t::setBlob(const void* key, EGLsizei keySize, const void* value,
if (mInitialized) {
sp<BlobCache> bc = getBlobCacheLocked();
bc->set(key, keySize, value, valueSize);
+
+ if (!mSavePending) {
+ class DeferredSaveThread : public Thread {
+ public:
+ DeferredSaveThread() : Thread(false) {}
+
+ virtual bool threadLoop() {
+ sleep(deferredSaveDelay);
+ egl_cache_t* c = egl_cache_t::get();
+ Mutex::Autolock lock(c->mMutex);
+ if (c->mInitialized) {
+ c->saveBlobCacheLocked();
+ }
+ c->mSavePending = false;
+ return false;
+ }
+ };
+
+ // The thread will hold a strong ref to itself until it has finished
+ // running, so there's no need to keep a ref around.
+ sp<Thread> deferredSaveThread(new DeferredSaveThread());
+ mSavePending = true;
+ deferredSaveThread->run();
+ }
}
}
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 05d5873..4389623 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -108,6 +108,13 @@ private:
// from disk.
String8 mFilename;
+ // mSavePending indicates whether or not a deferred save operation is
+ // pending. Each time a key/value pair is inserted into the cache via
+ // setBlob, a deferred save is initiated if one is not already pending.
+ // This will wait some amount of time and then trigger a save of the cache
+ // contents to disk.
+ bool mSavePending;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables. It must be locked whenever the member variables are accessed.
mutable Mutex mMutex;
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png
index 60bd807..88137e8 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png
index 7fb5b20..6507a51 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png
index 8354fef..798f589 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png
index f32980d..73247e5 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png
index dbfce78..2b46c89 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png
index 37313e9..dd476b7 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index dafbdcf..a7da96e 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -91,7 +91,7 @@ class KeyguardStatusViewManager implements OnClickListener {
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private Button mEmergencyCallButton;
- private boolean mUnlockDisabledDueToSimState;
+ private boolean mEmergencyButtonEnabledBecauseSimLocked;
// Shadowed text values
private CharSequence mCarrierText;
@@ -101,9 +101,10 @@ class KeyguardStatusViewManager implements OnClickListener {
private CharSequence mOwnerInfoText;
private boolean mShowingStatus;
private KeyguardScreenCallback mCallback;
- private final boolean mShowEmergencyButtonByDefault;
+ private final boolean mEmergencyCallButtonEnabledInScreen;
private CharSequence mPlmn;
private CharSequence mSpn;
+ protected int mPhoneState;
private class TransientTextManager {
private TextView mTextView;
@@ -154,9 +155,17 @@ class KeyguardStatusViewManager implements OnClickListener {
}
};
+ /**
+ *
+ * @param view the containing view of all widgets
+ * @param updateMonitor the update monitor to use
+ * @param lockPatternUtils lock pattern util object
+ * @param callback used to invoke emergency dialer
+ * @param emergencyButtonEnabledInScreen whether emergency button is enabled by default
+ */
public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,
- boolean showEmergencyButtonByDefault) {
+ boolean emergencyButtonEnabledInScreen) {
if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
@@ -171,7 +180,7 @@ class KeyguardStatusViewManager implements OnClickListener {
mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
- mShowEmergencyButtonByDefault = showEmergencyButtonByDefault;
+ mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
// Hide transport control view until we know we need to show it.
if (mTransportView != null) {
@@ -452,12 +461,12 @@ class KeyguardStatusViewManager implements OnClickListener {
*
* @param simState
*/
- private void updateCarrierTextWithSimStatus(State simState) {
+ private void updateCarrierStateWithSimStatus(State simState) {
if (DEBUG) Log.d(TAG, "updateCarrierTextWithSimStatus(), simState = " + simState);
CharSequence carrierText = null;
int carrierHelpTextId = 0;
- mUnlockDisabledDueToSimState = false;
+ mEmergencyButtonEnabledBecauseSimLocked = false;
mStatus = getStatusForIccState(simState);
mSimState = simState;
switch (mStatus) {
@@ -479,32 +488,35 @@ class KeyguardStatusViewManager implements OnClickListener {
case SimPermDisabled:
carrierText = getContext().getText(R.string.lockscreen_missing_sim_message_short);
carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions;
- mUnlockDisabledDueToSimState = true;
+ mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimMissingLocked:
carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_missing_sim_message_short));
carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
- mUnlockDisabledDueToSimState = true;
+ mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimLocked:
carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_sim_locked_message));
+ mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimPukLocked:
carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_sim_puk_locked_message));
if (!mLockPatternUtils.isPukUnlockScreenEnable()) {
- mUnlockDisabledDueToSimState = true;
+ // This means we're showing the PUK unlock screen
+ mEmergencyButtonEnabledBecauseSimLocked = true;
}
break;
}
setCarrierText(carrierText);
setCarrierHelpText(carrierHelpTextId);
+ updateEmergencyCallButtonState(mPhoneState);
}
private View findViewById(int id) {
@@ -569,9 +581,12 @@ class KeyguardStatusViewManager implements OnClickListener {
private void updateEmergencyCallButtonState(int phoneState) {
if (mEmergencyCallButton != null) {
- boolean showIfCapable = mShowEmergencyButtonByDefault || mUnlockDisabledDueToSimState;
+ boolean enabledBecauseSimLocked =
+ mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked()
+ && mEmergencyButtonEnabledBecauseSimLocked;
+ boolean shown = mEmergencyCallButtonEnabledInScreen || enabledBecauseSimLocked;
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton,
- phoneState, showIfCapable);
+ phoneState, shown);
}
}
@@ -594,7 +609,7 @@ class KeyguardStatusViewManager implements OnClickListener {
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
mPlmn = plmn;
mSpn = spn;
- updateCarrierTextWithSimStatus(mSimState);
+ updateCarrierStateWithSimStatus(mSimState);
}
public void onRingerModeChanged(int state) {
@@ -602,6 +617,7 @@ class KeyguardStatusViewManager implements OnClickListener {
}
public void onPhoneStateChanged(int phoneState) {
+ mPhoneState = phoneState;
updateEmergencyCallButtonState(phoneState);
}
@@ -618,7 +634,7 @@ class KeyguardStatusViewManager implements OnClickListener {
private SimStateCallback mSimStateCallback = new SimStateCallback() {
public void onSimStateChanged(State simState) {
- updateCarrierTextWithSimStatus(simState);
+ updateCarrierStateWithSimStatus(simState);
}
};
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 81e1901..0f21bdb 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -360,8 +360,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
mHasOverlay = true;
// Continue showing FaceLock area until dialer comes up or call is resumed
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled() && mFaceLockServiceRunning) {
+ if (usingFaceLock() && mFaceLockServiceRunning) {
showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT);
}
@@ -582,8 +581,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
bindToFaceLock();
// Show FaceLock area, but only for a little bit so lockpattern will become visible if
// FaceLock fails to start or crashes
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (usingFaceLock()) {
showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT);
}
} else {
@@ -653,11 +651,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
((KeyguardScreen) mUnlockScreen).onResume();
}
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled() && !mHasOverlay) {
+ if (usingFaceLock() && !mHasOverlay) {
// Note that show() gets called before the screen turns off to set it up for next time
// it is turned on. We don't want to set a timeout on the FaceLock area here because it
- // may be gone by the time the screen is turned on again. We set the timout when the
+ // may be gone by the time the screen is turned on again. We set the timeout when the
// screen turns on instead.
showFaceLockArea();
} else {
@@ -854,7 +851,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
if (mode == Mode.UnlockScreen) {
final UnlockMode unlockMode = getUnlockMode();
if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
+ boolean restartFaceLock = stopFaceLockIfRunning();
recreateUnlockScreen(unlockMode);
+ if (restartFaceLock) activateFaceLockIfAble();
}
}
@@ -1147,28 +1146,33 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
// Everything below pertains to FaceLock - might want to separate this out
- // Take care of FaceLock area when layout is created
+ // Indicates whether FaceLock is in use
+ private boolean usingFaceLock() {
+ return (mLockPatternUtils.usingBiometricWeak() &&
+ mLockPatternUtils.isBiometricWeakInstalled());
+ }
+
+ // Takes care of FaceLock area when layout is created
private void initializeFaceLockAreaView(View view) {
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (usingFaceLock()) {
mFaceLockAreaView = view.findViewById(R.id.faceLockAreaView);
if (mFaceLockAreaView == null) {
Log.e(TAG, "Layout does not have faceLockAreaView and FaceLock is enabled");
- } else {
- if (mBoundToFaceLockService) {
- // If we are creating a layout when we are already bound to FaceLock, then we
- // are undergoing an orientation change. Stop FaceLock and restart it in the
- // new location.
- if (DEBUG) Log.d(TAG, "Restarting FL - creating view while already bound");
- stopAndUnbindFromFaceLock();
- activateFaceLockIfAble();
- }
}
} else {
mFaceLockAreaView = null; // Set to null if not using FaceLock
}
}
+ // Stops FaceLock if it is running and reports back whether it was running or not
+ private boolean stopFaceLockIfRunning() {
+ if (usingFaceLock() && mBoundToFaceLockService) {
+ stopAndUnbindFromFaceLock();
+ return true;
+ }
+ return false;
+ }
+
// Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
// This needs to be done in a handler because the call could be coming from a callback from the
// FaceLock service that is in a thread that can't modify the UI
@@ -1221,8 +1225,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
// Binds to FaceLock service. This call does not tell it to start, but it causes the service
// to call the onServiceConnected callback, which then starts FaceLock.
public void bindToFaceLock() {
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (usingFaceLock()) {
if (!mBoundToFaceLockService) {
if (DEBUG) Log.d(TAG, "before bind to FaceLock service");
mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
@@ -1238,8 +1241,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
// Tells FaceLock to stop and then unbinds from the FaceLock service
public void stopAndUnbindFromFaceLock() {
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (usingFaceLock()) {
stopFaceLock();
if (mBoundToFaceLockService) {
@@ -1300,8 +1302,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
// Tells the FaceLock service to start displaying its UI and perform recognition
public void startFaceLock(IBinder windowToken, int x, int y, int h, int w)
{
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (usingFaceLock()) {
synchronized (mFaceLockServiceRunningLock) {
if (!mFaceLockServiceRunning) {
if (DEBUG) Log.d(TAG, "Starting FaceLock");
@@ -1322,8 +1323,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
// Tells the FaceLock service to stop displaying its UI and stop recognition
public void stopFaceLock()
{
- if (mLockPatternUtils.usingBiometricWeak() &&
- mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (usingFaceLock()) {
// Note that attempting to stop FaceLock when it's not running is not an issue.
// FaceLock can return, which stops it and then we try to stop it when the
// screen is turned off. That's why we check.
diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
index 6acd1c5..47a7157 100644
--- a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
@@ -106,7 +106,7 @@ public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen,
mHeaderText.setSelected(true);
mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, updateMonitor,
- lockpatternutils, callback, true);
+ lockpatternutils, callback, false);
mPinText.setFocusableInTouchMode(true);
mPinText.setOnFocusChangeListener(this);
diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
index 184748a..99e1ce1 100644
--- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
@@ -100,7 +100,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
mOkButton.setOnClickListener(this);
mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, updateMonitor,
- lockpatternutils, callback, true);
+ lockpatternutils, callback, false);
setFocusableInTouchMode(true);
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 96e8eb9..ff262f1 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2066,9 +2066,14 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// The first time a track is added we wait
// for all its buffers to be filled before processing it
mAudioMixer->setActiveTrack(track->name());
- // make sure that we have enough frames to mix one full buffer
+ // make sure that we have enough frames to mix one full buffer.
+ // enforce this condition only once to enable draining the buffer in case the client
+ // app does not call stop() and relies on underrun to stop:
+ // hence the test on (track->mRetryCount >= kMaxTrackRetries) meaning the track was mixed
+ // during last round
uint32_t minFrames = 1;
- if (!track->isStopped() && !track->isPausing()) {
+ if (!track->isStopped() && !track->isPausing() &&
+ (track->mRetryCount >= kMaxTrackRetries)) {
if (t->sampleRate() == (int)mSampleRate) {
minFrames = mFrameCount;
} else {
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 34f8848..f2ccb5b 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1513,14 +1513,65 @@ public class PhoneNumberUtils
static final int MIN_MATCH = 7;
/**
- * isEmergencyNumber: checks a given number against the list of
- * emergency numbers provided by the RIL and SIM card.
+ * Checks a given number against the list of
+ * emergency numbers provided by the RIL and SIM card.
*
* @param number the number to look up.
- * @return if the number is in the list of emergency numbers
- * listed in the ril / sim, then return true, otherwise false.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, otherwise return false.
*/
public static boolean isEmergencyNumber(String number) {
+ // Return true only if the specified number *exactly* matches
+ // one of the emergency numbers listed by the RIL / SIM.
+ return isEmergencyNumberInternal(number, true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if given number might *potentially* result in
+ * a call to an emergency service on the current network.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number according to the list managed by the RIL or
+ * SIM, *or* if the specified number simply starts with the same
+ * digits as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, *or* if the number starts with the
+ * same digits as any of those emergency numbers.
+ *
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(String number) {
+ // Check against the emergency numbers listed by the RIL / SIM,
+ // and *don't* require an exact match.
+ return isEmergencyNumberInternal(number, false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String) and
+ * isPotentialEmergencyNumber(String).
+ *
+ * @param number the number to look up.
+ *
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ * (Setting useExactMatch to false allows you to identify
+ * number that could *potentially* result in emergency calls
+ * since many networks will actually ignore trailing digits
+ * after a valid emergency number.)
+ *
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / sim, otherwise return false.
+ */
+ private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
// If the number passed in is null, just return false:
if (number == null) return false;
@@ -1540,16 +1591,26 @@ public class PhoneNumberUtils
// searches through the comma-separated list for a match,
// return true if one is found.
for (String emergencyNum : numbers.split(",")) {
- if (number.startsWith(emergencyNum)) {
- return true;
+ if (useExactMatch) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
}
}
// no matches found against the list!
return false;
}
- //no ecclist system property, so use our own list.
- return (number.startsWith("112") || number.startsWith("911"));
+ // No ecclist system property, so use our own list.
+ if (useExactMatch) {
+ return (number.equals("112") || number.equals("911"));
+ } else {
+ return (number.startsWith("112") || number.startsWith("911"));
+ }
}
/**
@@ -1559,31 +1620,81 @@ public class PhoneNumberUtils
* @param defaultCountryIso the specific country which the number should be checked against
* @return if the number is an emergency number for the specific country, then return true,
* otherwise false
+ *
* @hide
*/
public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
- PhoneNumberUtil util = PhoneNumberUtil.getInstance();
- try {
- PhoneNumber pn = util.parse(number, defaultCountryIso);
- // libphonenumber guarantees short numbers such as emergency numbers are classified as
- // invalid. Therefore, if the number passes the validation test, we believe it is not an
- // emergency number.
- // TODO: Compare against a list of country-specific known emergency numbers instead, once
- // that has been collected.
- if (util.isValidNumber(pn)) {
- return false;
- } else if ("BR".equalsIgnoreCase(defaultCountryIso) && number.length() >= 8) {
- // This is to prevent Brazilian local numbers which start with 911 being incorrectly
- // classified as emergency numbers. 911 is not an emergency number in Brazil; it is also
- // not possible to append additional digits to an emergency number to dial the number in
- // Brazil - it won't connect.
- // TODO: Clean this up once a list of country-specific known emergency numbers is
- // collected.
- return false;
- }
- } catch (NumberParseException e) {
- }
- return isEmergencyNumber(number);
+ return isEmergencyNumberInternal(number,
+ defaultCountryIso,
+ true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for a specific country.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the specified country, *or* if the number
+ * simply starts with the same digits as any emergency number for that
+ * country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @return true if the number is an emergency number for the specific
+ * country, *or* if the number starts with the same digits as
+ * any of those emergency numbers.
+ *
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
+ return isEmergencyNumberInternal(number,
+ defaultCountryIso,
+ false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String, String) and
+ * isPotentialEmergencyNumber(String, String).
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the number is an emergency number for the specified country.
+ */
+ private static boolean isEmergencyNumberInternal(String number,
+ String defaultCountryIso,
+ boolean useExactMatch) {
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ try {
+ PhoneNumber pn = util.parse(number, defaultCountryIso);
+ // libphonenumber guarantees short numbers such as emergency numbers are classified as
+ // invalid. Therefore, if the number passes the validation test, we believe it is not an
+ // emergency number.
+ // TODO: Compare against a list of country-specific known emergency numbers instead, once
+ // that has been collected.
+ if (util.isValidNumber(pn)) {
+ return false;
+ } else if ("BR".equalsIgnoreCase(defaultCountryIso) && number.length() >= 8) {
+ // This is to prevent Brazilian local numbers which start with 911 being incorrectly
+ // classified as emergency numbers. 911 is not an emergency number in Brazil; it is also
+ // not possible to append additional digits to an emergency number to dial the number in
+ // Brazil - it won't connect.
+ // TODO: Clean this up once a list of country-specific known emergency numbers is
+ // collected.
+ return false;
+ }
+ } catch (NumberParseException e) {
+ }
+ return isEmergencyNumberInternal(number, useExactMatch);
}
/**
@@ -1592,12 +1703,66 @@ public class PhoneNumberUtils
*
* @param number the number to look up.
* @param context the specific context which the number should be checked against
- * @return if a phone number is an emergency number for a local country, based on the
- * CountryDetector.
+ * @return true if the specified number is an emergency number for a local country, based on the
+ * CountryDetector.
+ *
* @see android.location.CountryDetector
* @hide
*/
public static boolean isLocalEmergencyNumber(String number, Context context) {
+ return isLocalEmergencyNumberInternal(number,
+ context,
+ true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for the country that the user is in. The current
+ * country is determined using the CountryDetector.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the current country, *or* if the number
+ * simply starts with the same digits as any emergency number for the
+ * current country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @return true if the specified number is an emergency number for a local country, based on the
+ * CountryDetector.
+ *
+ * @see android.location.CountryDetector
+ * @hide
+ */
+ public static boolean isPotentialLocalEmergencyNumber(String number, Context context) {
+ return isLocalEmergencyNumberInternal(number,
+ context,
+ false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isLocalEmergencyNumber() and
+ * isPotentialLocalEmergencyNumber().
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the specified number is an emergency number for a
+ * local country, based on the CountryDetector.
+ *
+ * @see android.location.CountryDetector
+ */
+ private static boolean isLocalEmergencyNumberInternal(String number,
+ Context context,
+ boolean useExactMatch) {
String countryIso;
CountryDetector detector = (CountryDetector) context.getSystemService(
Context.COUNTRY_DETECTOR);
@@ -1609,7 +1774,7 @@ public class PhoneNumberUtils
Log.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
+ countryIso);
}
- return isEmergencyNumber(number, countryIso);
+ return isEmergencyNumberInternal(number, countryIso, useExactMatch);
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index fc8a145..1410747 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -291,12 +291,31 @@ public class SmsMessage {
// flexibly...
int limit;
- if (ted.msgCount > 1) {
- limit = (ted.codeUnitSize == ENCODING_7BIT) ?
- MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER;
+ if (ted.codeUnitSize == ENCODING_7BIT) {
+ int udhLength;
+ if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
+ udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+ } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
+ udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+ } else {
+ udhLength = 0;
+ }
+
+ if (ted.msgCount > 1) {
+ udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+ }
+
+ if (udhLength != 0) {
+ udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
+ }
+
+ limit = MAX_USER_DATA_SEPTETS - udhLength;
} else {
- limit = (ted.codeUnitSize == ENCODING_7BIT) ?
- MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES;
+ if (ted.msgCount > 1) {
+ limit = MAX_USER_DATA_BYTES_WITH_HEADER;
+ } else {
+ limit = MAX_USER_DATA_BYTES;
+ }
}
int pos = 0; // Index in code units.
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 2e99849..25647ac 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -60,25 +60,25 @@ public class GsmAlphabet {
* all combinations of header elements below will have at least one free bit
* when padding to the nearest septet boundary.
*/
- private static final int UDH_SEPTET_COST_LENGTH = 1;
+ public static final int UDH_SEPTET_COST_LENGTH = 1;
/**
* Using a non-default language locking shift table OR single shift table
* requires a user data header of 3 octets, or 4 septets, plus UDH length.
*/
- private static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
+ public static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
/**
* Using a non-default language locking shift table AND single shift table
* requires a user data header of 6 octets, or 7 septets, plus UDH length.
*/
- private static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
+ public static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
/**
* Multi-part messages require a user data header of 5 octets, or 6 septets,
* plus UDH length.
*/
- private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
+ public static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
/**
* Converts a char to a GSM 7 bit table index.
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index 41a719e..5950669 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -16,11 +16,14 @@
package com.android.internal.telephony;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.internal.telephony.gsm.SmsMessage;
import com.android.internal.util.HexDump;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import java.util.ArrayList;
public class GsmSmsTest extends AndroidTestCase {
@@ -232,6 +235,110 @@ public class GsmSmsTest extends AndroidTestCase {
};
@SmallTest
+ public void testFragmentText() throws Exception {
+ boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
+ TelephonyManager.PHONE_TYPE_GSM);
+
+ // Valid 160 character 7-bit text.
+ String text = "123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901234567890123456789012345678901234567890" +
+ "123456789012345678901234567890";
+ SmsMessageBase.TextEncodingDetails ted = SmsMessage.calculateLength(text, false);
+ assertEquals(1, ted.msgCount);
+ assertEquals(160, ted.codeUnitCount);
+ assertEquals(1, ted.codeUnitSize);
+ assertEquals(0, ted.languageTable);
+ assertEquals(0, ted.languageShiftTable);
+ if (isGsmPhone) {
+ ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
+ assertEquals(1, fragments.size());
+ }
+
+ // Valid 161 character 7-bit text.
+ text = "123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901";
+ ted = SmsMessage.calculateLength(text, false);
+ assertEquals(2, ted.msgCount);
+ assertEquals(161, ted.codeUnitCount);
+ assertEquals(1, ted.codeUnitSize);
+ assertEquals(0, ted.languageTable);
+ assertEquals(0, ted.languageShiftTable);
+ if (isGsmPhone) {
+ ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
+ assertEquals(2, fragments.size());
+ assertEquals(text, fragments.get(0) + fragments.get(1));
+ assertEquals(153, fragments.get(0).length());
+ assertEquals(8, fragments.get(1).length());
+ }
+ }
+
+ @SmallTest
+ public void testFragmentTurkishText() throws Exception {
+ boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
+ TelephonyManager.PHONE_TYPE_GSM);
+
+ int[] oldTables = GsmAlphabet.getEnabledSingleShiftTables();
+ int[] turkishTable = { 1 };
+ GsmAlphabet.setEnabledSingleShiftTables(turkishTable);
+
+ // Valid 77 character text with Turkish characters.
+ String text = "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı" +
+ "ĞŞİğşıĞŞİğşıĞŞİğş";
+ SmsMessageBase.TextEncodingDetails ted = SmsMessage.calculateLength(text, false);
+ assertEquals(1, ted.msgCount);
+ assertEquals(154, ted.codeUnitCount);
+ assertEquals(1, ted.codeUnitSize);
+ assertEquals(0, ted.languageTable);
+ assertEquals(1, ted.languageShiftTable);
+ if (isGsmPhone) {
+ ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
+ assertEquals(1, fragments.size());
+ assertEquals(text, fragments.get(0));
+ assertEquals(77, fragments.get(0).length());
+ }
+
+ // Valid 78 character text with Turkish characters.
+ text = "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı" +
+ "ĞŞİğşıĞŞİğşıĞŞİğşı";
+ ted = SmsMessage.calculateLength(text, false);
+ assertEquals(2, ted.msgCount);
+ assertEquals(156, ted.codeUnitCount);
+ assertEquals(1, ted.codeUnitSize);
+ assertEquals(0, ted.languageTable);
+ assertEquals(1, ted.languageShiftTable);
+ if (isGsmPhone) {
+ ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
+ assertEquals(2, fragments.size());
+ assertEquals(text, fragments.get(0) + fragments.get(1));
+ assertEquals(74, fragments.get(0).length());
+ assertEquals(4, fragments.get(1).length());
+ }
+
+ // Valid 160 character text with Turkish characters.
+ text = "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı" +
+ "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğ" +
+ "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı";
+ ted = SmsMessage.calculateLength(text, false);
+ assertEquals(3, ted.msgCount);
+ assertEquals(320, ted.codeUnitCount);
+ assertEquals(1, ted.codeUnitSize);
+ assertEquals(0, ted.languageTable);
+ assertEquals(1, ted.languageShiftTable);
+ if (isGsmPhone) {
+ ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
+ assertEquals(3, fragments.size());
+ assertEquals(text, fragments.get(0) + fragments.get(1) + fragments.get(2));
+ assertEquals(74, fragments.get(0).length());
+ assertEquals(74, fragments.get(1).length());
+ assertEquals(12, fragments.get(2).length());
+ }
+
+ GsmAlphabet.setEnabledSingleShiftTables(oldTables);
+ }
+
+
+ @SmallTest
public void testDecode() throws Exception {
decodeSingle(0); // default table
decodeSingle(1); // Turkish locking shift table
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index e2349af..d34a7c5 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -550,21 +550,51 @@ public class PhoneNumberUtilsTest extends AndroidTestCase {
}
@SmallTest
public void testIsEmergencyNumber() {
- assertTrue(PhoneNumberUtils.isEmergencyNumber("911", "US"));
- assertTrue(PhoneNumberUtils.isEmergencyNumber("112", "US"));
- // The next two numbers are not valid phone numbers in the US, but can be used to trick the
- // system to dial 911 and 112, which are emergency numbers in the US. For the purpose of
- // addressing that, they are also classified as emergency numbers in the US.
- assertTrue(PhoneNumberUtils.isEmergencyNumber("91112345", "US"));
- assertTrue(PhoneNumberUtils.isEmergencyNumber("11212345", "US"));
- // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
- // in Singapore, as 911 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234", "SG"));
- // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
- // in Brazil, as 112 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567", "BR"));
- // A valid local phone number from Brazil shouldn't be classified as an emergency number in
- // Brazil.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "BR"));
+ // There are two parallel sets of tests here: one for the
+ // regular isEmergencyNumber() method, and the other for
+ // isPotentialEmergencyNumber().
+ //
+ // (The difference is that isEmergencyNumber() will return true
+ // only if the specified number exactly matches an actual
+ // emergency number, but isPotentialEmergencyNumber() will
+ // return true if the specified number simply starts with the
+ // same digits as any actual emergency number.)
+
+ // Tests for isEmergencyNumber():
+ assertTrue(PhoneNumberUtils.isEmergencyNumber("911", "US"));
+ assertTrue(PhoneNumberUtils.isEmergencyNumber("112", "US"));
+ // The next two numbers are not valid phone numbers in the US,
+ // so do not count as emergency numbers (but they *are* "potential"
+ // emergency numbers; see below.)
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "US"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("11212345", "US"));
+ // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
+ // in Singapore, as 911 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234", "SG"));
+ // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
+ // in Brazil, as 112 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567", "BR"));
+ // A valid local phone number from Brazil shouldn't be classified as an emergency number in
+ // Brazil.
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "BR"));
+
+ // Tests for isPotentialEmergencyNumber():
+ // These first two are obviously emergency numbers:
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("911", "US"));
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("112", "US"));
+ // The next two numbers are not valid phone numbers in the US, but can be used to trick the
+ // system to dial 911 and 112, which are emergency numbers in the US. For the purpose of
+ // addressing that, they are also classified as "potential" emergency numbers in the US.
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "US"));
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("11212345", "US"));
+ // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
+ // in Singapore, as 911 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG"));
+ // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
+ // in Brazil, as 112 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("1121234567", "BR"));
+ // A valid local phone number from Brazil shouldn't be classified as an emergency number in
+ // Brazil.
+ assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "BR"));
}
}