am 399df567: docs: various link fixes and other typos from external tracker
* commit '399df5677bd116a016664a10a0da432da29e829d':
docs: various link fixes and other typos from external tracker
diff --git a/api/current.txt b/api/current.txt
index bc97e81..880170d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20282,6 +20282,7 @@
public class CdmaCellLocation extends android.telephony.CellLocation {
ctor public CdmaCellLocation();
ctor public CdmaCellLocation(android.os.Bundle);
+ method public static double convertQuartSecToDecDegrees(int);
method public void fillInNotifierBundle(android.os.Bundle);
method public int getBaseStationId();
method public int getBaseStationLatitude();
@@ -24096,6 +24097,7 @@
method public long getDrawingTime();
method public boolean getFilterTouchesWhenObscured();
method public boolean getFitsSystemWindows();
+ method public void getFocusRect(android.graphics.Rect);
method public java.util.ArrayList<android.view.View> getFocusables(int);
method public void getFocusedRect(android.graphics.Rect);
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index f277339..3e722ea 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -34,6 +34,12 @@
LOCAL_STATIC_LIBRARIES := \
libdiskusage
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_SHARED_LIBRARIES += libselinux
+LOCAL_CFLAGS := -DHAVE_SELINUX
+endif # HAVE_SELINUX
+
LOCAL_MODULE := installd
LOCAL_MODULE_TAGS := optional
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 96a6438..1bb4935 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -17,6 +17,10 @@
#include "installd.h"
#include <diskusage/dirsize.h>
+#ifdef HAVE_SELINUX
+#include <selinux/android.h>
+#endif
+
/* Directory records that are used in execution of commands. */
dir_rec_t android_data_dir;
dir_rec_t android_asec_dir;
@@ -72,12 +76,31 @@
return -errno;
}
+#ifdef HAVE_SELINUX
+ if (selinux_android_setfilecon(libdir, pkgname, AID_SYSTEM) < 0) {
+ LOGE("cannot setfilecon dir '%s': %s\n", libdir, strerror(errno));
+ unlink(libdir);
+ unlink(pkgdir);
+ return -errno;
+ }
+#endif
+
if (chown(pkgdir, uid, gid) < 0) {
ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
unlink(libdir);
unlink(pkgdir);
return -errno;
}
+
+#ifdef HAVE_SELINUX
+ if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
+ LOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(libdir);
+ unlink(pkgdir);
+ return -errno;
+ }
+#endif
+
return 0;
}
@@ -175,6 +198,15 @@
unlink(pkgdir);
return -errno;
}
+
+#ifdef HAVE_SELINUX
+ if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
+ LOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
+#endif
+
return 0;
}
@@ -366,12 +398,18 @@
ALOGE("failed to chgrp '%s': %s\n", pkgpath, strerror(errno));
return -1;
}
-
if (chmod(pkgpath, S_IRUSR|S_IWUSR|S_IRGRP) < 0) {
ALOGE("failed to chmod '%s': %s\n", pkgpath, strerror(errno));
return -1;
}
+#ifdef HAVE_SELINUX
+ if (selinux_android_setfilecon(pkgpath, pkgname, s.st_uid) < 0) {
+ LOGE("cannot setfilecon dir '%s': %s\n", pkgpath, strerror(errno));
+ return -1;
+ }
+#endif
+
return 0;
}
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index dd9ea26..3a67cec 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -64,11 +64,12 @@
}
/**
- * Called when the application is starting, before any other application
- * objects have been created. Implementations should be as quick as
- * possible (for example using lazy initialization of state) since the time
- * spent in this function directly impacts the performance of starting the
- * first activity, service, or receiver in a process.
+ * Called when the application is starting, before any activity, service,
+ * or receiver objects (excluding content providers) have been created.
+ * Implementations should be as quick as possible (for example using
+ * lazy initialization of state) since the time spent in this function
+ * directly impacts the performance of starting the first activity,
+ * service, or receiver in a process.
* If you override this method, be sure to call super.onCreate().
*/
public void onCreate() {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 3e726e0..8e6278d 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -385,6 +385,7 @@
case SCHEDULE_LOW_MEMORY_TRANSACTION:
{
+ data.enforceInterface(IApplicationThread.descriptor);
scheduleLowMemory();
return true;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
old mode 100644
new mode 100755
index 4ed0766..0b58396
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1008,13 +1008,15 @@
/**
* Ask the user date be wiped. This will cause the device to reboot,
* erasing all user data while next booting up. External storage such
- * as SD cards will not be erased.
+ * as SD cards will be also erased if the flag {@link #WIPE_EXTERNAL_STORAGE}
+ * is set.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call
* this method; if it has not, a security exception will be thrown.
*
- * @param flags Bit mask of additional options: currently must be 0.
+ * @param flags Bit mask of additional options: currently 0 and
+ * {@link #WIPE_EXTERNAL_STORAGE} are supported.
*/
public void wipeData(int flags) {
if (mService != null) {
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index c9603bf..020f051 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -304,6 +304,25 @@
}
}
+ @Override
+ protected void onQuitting() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ mBroadcastReceiver = null;
+ mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService);
+ mBluetoothProfileServiceListener = null;
+ mOutgoingHandsfree = null;
+ mPbap = null;
+ mPbapService.close();
+ mPbapService = null;
+ mIncomingHid = null;
+ mOutgoingHid = null;
+ mIncomingHandsfree = null;
+ mOutgoingHandsfree = null;
+ mIncomingA2dp = null;
+ mOutgoingA2dp = null;
+ mBondedDevice = null;
+ }
+
private class BondedDevice extends State {
@Override
public void enter() {
@@ -416,26 +435,6 @@
case TRANSITION_TO_STABLE:
// ignore.
break;
- case SM_QUIT_CMD:
- mContext.unregisterReceiver(mBroadcastReceiver);
- mBroadcastReceiver = null;
- mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService);
- mBluetoothProfileServiceListener = null;
- mOutgoingHandsfree = null;
- mPbap = null;
- mPbapService.close();
- mPbapService = null;
- mIncomingHid = null;
- mOutgoingHid = null;
- mIncomingHandsfree = null;
- mOutgoingHandsfree = null;
- mIncomingA2dp = null;
- mOutgoingA2dp = null;
- mBondedDevice = null;
- // There is a problem in the State Machine code
- // where things are not cleaned up properly, when quit message
- // is handled so return NOT_HANDLED as a workaround.
- return NOT_HANDLED;
default:
return NOT_HANDLED;
}
@@ -1341,6 +1340,15 @@
return mDevice;
}
+ /**
+ * Quit the state machine, only to be called by BluetoothService.
+ *
+ * @hide
+ */
+ public void doQuit() {
+ this.quit();
+ }
+
private void log(String message) {
if (DBG) {
Log.i(TAG, "Device:" + mDevice + " Message:" + message);
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 397a12a..cc3e34f 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -163,8 +163,21 @@
mRegisteredForPreDhcpNotification = true;
}
+ /**
+ * Quit the DhcpStateMachine.
+ *
+ * @hide
+ */
+ public void doQuit() {
+ quit();
+ }
+
class DefaultState extends State {
@Override
+ public void exit() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
+ @Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
@@ -172,10 +185,6 @@
Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
mDhcpRenewWakeLock.release();
break;
- case SM_QUIT_CMD:
- mContext.unregisterReceiver(mBroadcastReceiver);
- //let parent kill the state machine
- return NOT_HANDLED;
default:
Log.e(TAG, "Error! unhandled message " + message);
break;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8eaeb1d..6ab4dc1 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -359,6 +359,7 @@
* @param gids Additional group-ids associated with the process.
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
+ * @param seInfo null-ok SE Android information for the new process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -370,10 +371,11 @@
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int targetSdkVersion,
+ String seInfo,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, targetSdkVersion, zygoteArgs);
+ debugFlags, targetSdkVersion, seInfo, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -536,6 +538,7 @@
* new process should setgroup() to.
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
+ * @param seInfo null-ok SE Android information for the new process.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -545,6 +548,7 @@
final int uid, final int gid,
final int[] gids,
int debugFlags, int targetSdkVersion,
+ String seInfo,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
@@ -595,6 +599,10 @@
argsForZygote.add("--nice-name=" + niceName);
}
+ if (seInfo != null) {
+ argsForZygote.add("--seinfo=" + seInfo);
+ }
+
argsForZygote.add(processClass);
if (extraArgs != null) {
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
new file mode 100644
index 0000000..90cfa37
--- /dev/null
+++ b/core/java/android/os/SELinux.java
@@ -0,0 +1,105 @@
+package android.os;
+
+import java.io.FileDescriptor;
+
+/**
+ * This class provides access to the centralized jni bindings for
+ * SELinux interaction.
+ * {@hide}
+ */
+public class SELinux {
+
+ /**
+ * Determine whether SELinux is disabled or enabled.
+ * @return a boolean indicating whether SELinux is enabled.
+ */
+ public static final native boolean isSELinuxEnabled();
+
+ /**
+ * Determine whether SELinux is permissive or enforcing.
+ * @return a boolean indicating whether SELinux is enforcing.
+ */
+ public static final native boolean isSELinuxEnforced();
+
+ /**
+ * Set whether SELinux is permissive or enforcing.
+ * @param boolean representing whether to set SELinux to enforcing
+ * @return a boolean representing whether the desired mode was set
+ */
+ public static final native boolean setSELinuxEnforce(boolean value);
+
+ /**
+ * Sets the security context for newly created file objects.
+ * @param context a security context given as a String.
+ * @return a boolean indicating whether the operation succeeded.
+ */
+ public static final native boolean setFSCreateContext(String context);
+
+ /**
+ * Change the security context of an existing file object.
+ * @param path representing the path of file object to relabel.
+ * @param con new security context given as a String.
+ * @return a boolean indicating whether the operation succeeded.
+ */
+ public static final native boolean setFileContext(String path, String context);
+
+ /**
+ * Get the security context of a file object.
+ * @param path the pathname of the file object.
+ * @return a security context given as a String.
+ */
+ public static final native String getFileContext(String path);
+
+ /**
+ * Get the security context of a peer socket.
+ * @param fd FileDescriptor class of the peer socket.
+ * @return a String representing the peer socket security context.
+ */
+ public static final native String getPeerContext(FileDescriptor fd);
+
+ /**
+ * Gets the security context of the current process.
+ * @return a String representing the security context of the current process.
+ */
+ public static final native String getContext();
+
+ /**
+ * Gets the security context of a given process id.
+ * Use of this function is discouraged for Binder transactions.
+ * Use Binder.getCallingSecctx() instead.
+ * @param pid an int representing the process id to check.
+ * @return a String representing the security context of the given pid.
+ */
+ public static final native String getPidContext(int pid);
+
+ /**
+ * Gets a list of the SELinux boolean names.
+ * @return an array of strings containing the SELinux boolean names.
+ */
+ public static final native String[] getBooleanNames();
+
+ /**
+ * Gets the value for the given SELinux boolean name.
+ * @param String The name of the SELinux boolean.
+ * @return a boolean indicating whether the SELinux boolean is set.
+ */
+ public static final native boolean getBooleanValue(String name);
+
+ /**
+ * Sets the value for the given SELinux boolean name.
+ * @param String The name of the SELinux boolean.
+ * @param Boolean The new value of the SELinux boolean.
+ * @return a boolean indicating whether or not the operation succeeded.
+ */
+ public static final native boolean setBooleanValue(String name, boolean value);
+
+ /**
+ * Check permissions between two security contexts.
+ * @param scon The source or subject security context.
+ * @param tcon The target or object security context.
+ * @param tclass The object security class name.
+ * @param perm The permission name.
+ * @return a boolean indicating whether permission was granted.
+ */
+ public static final native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm);
+}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 97c0209..6296b11 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -2448,7 +2448,7 @@
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state == null) return;
- state.quit();
+ state.doQuit();
mDeviceProfileState.remove(address);
}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 9063cea..31a9f05 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -250,8 +250,8 @@
// only interested in other non-root views
if (focusable == focused || focusable == root) continue;
- // get visible bounds of other view in same coordinate system
- focusable.getDrawingRect(mOtherRect);
+ // get focus bounds of other view in same coordinate system
+ focusable.getFocusRect(mOtherRect);
root.offsetDescendantRectToMyCoords(focusable, mOtherRect);
if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b1caa2f..fa555d2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8737,6 +8737,18 @@
}
/**
+ * When searching for a view to focus this rectangle is used when considering if this view is
+ * a good candidate for receiving focus.
+ *
+ * By default, the rectangle is the {@link #getDrawingRect}) of the view.
+ *
+ * @param r The rectangle to fill in, in this view's coordinates.
+ */
+ public void getFocusRect(Rect r) {
+ getDrawingRect(r);
+ }
+
+ /**
* Utility method to retrieve the inverse of the current mMatrix property.
* We cache the matrix to avoid recalculating it when transform properties
* have not changed.
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 323fcf0..b72b8cb 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1192,15 +1192,15 @@
case KeyEvent.KEYCODE_DPAD_LEFT:
if (movePrevious()) {
playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
+ return true;
}
- return true;
-
+ break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (moveNext()) {
playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
+ return true;
}
- return true;
-
+ break;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
mReceivedInvokeKeyDown = true;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 8975109..ada7dd1 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -2207,8 +2207,13 @@
int height = view.getHeight();
if (height > 0) {
final int numColumns = mNumColumns;
- final int whichRow = mFirstPosition / numColumns;
final int rowCount = (mItemCount + numColumns - 1) / numColumns;
+ // In case of stackFromBottom the calculation of whichRow needs
+ // to take into account that counting from the top the first row
+ // might not be entirely filled.
+ final int oddItemsOnFirstRow = isStackFromBottom() ? ((rowCount * numColumns) -
+ mItemCount) : 0;
+ final int whichRow = (mFirstPosition + oddItemsOnFirstRow) / numColumns;
return Math.max(whichRow * 100 - (top * 100) / height +
(int) ((float) mScrollY / getHeight() * rowCount * 100), 0);
}
diff --git a/core/java/android/widget/SimpleExpandableListAdapter.java b/core/java/android/widget/SimpleExpandableListAdapter.java
index 015c169..f514374 100644
--- a/core/java/android/widget/SimpleExpandableListAdapter.java
+++ b/core/java/android/widget/SimpleExpandableListAdapter.java
@@ -38,6 +38,8 @@
*/
public class SimpleExpandableListAdapter extends BaseExpandableListAdapter {
private List<? extends Map<String, ?>> mGroupData;
+ // Keeps track of if a group is currently expanded or not
+ private boolean[] mIsGroupExpanded;
private int mExpandedGroupLayout;
private int mCollapsedGroupLayout;
private String[] mGroupFrom;
@@ -196,6 +198,8 @@
int childLayout, int lastChildLayout, String[] childFrom,
int[] childTo) {
mGroupData = groupData;
+ // Initially all groups are not expanded
+ mIsGroupExpanded = new boolean[groupData.size()];
mExpandedGroupLayout = expandedGroupLayout;
mCollapsedGroupLayout = collapsedGroupLayout;
mGroupFrom = groupFrom;
@@ -298,4 +302,52 @@
return true;
}
+ /**
+ * {@inheritDoc}
+ * @return 1 for the last child in a group, 0 for the other children.
+ */
+ @Override
+ public int getChildType(int groupPosition, int childPosition) {
+ final int childrenInGroup = getChildrenCount(groupPosition);
+ return childPosition == childrenInGroup - 1 ? 1 : 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 2, one type for the last child in a group, one for the other children.
+ */
+ @Override
+ public int getChildTypeCount() {
+ return 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 1 for an expanded group view, 0 for a collapsed one.
+ */
+ @Override
+ public int getGroupType(int groupPosition) {
+ return mIsGroupExpanded[groupPosition] ? 1 : 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 2, one for a collapsed group view, one for an expanded one.
+ */
+ @Override
+ public int getGroupTypeCount() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onGroupCollapsed(int groupPosition) {
+ mIsGroupExpanded[groupPosition] = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onGroupExpanded(int groupPosition) {
+ mIsGroupExpanded[groupPosition] = true;
+ }
}
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
index 1923b86..b1bb8c1 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -154,7 +154,7 @@
private boolean mFirst = true;
- private byte[] mBuffer = new byte[256];
+ private byte[] mBuffer = new byte[4096];
/**
* The time in microseconds that the CPU has been running at each speed.
@@ -556,7 +556,7 @@
private long[] getCpuSpeedTimes(long[] out) {
long[] tempTimes = out;
long[] tempSpeeds = mCpuSpeeds;
- final int MAX_SPEEDS = 20;
+ final int MAX_SPEEDS = 60;
if (out == null) {
tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that
tempSpeeds = new long[MAX_SPEEDS];
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 9af7e96..b016e99 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -26,6 +26,8 @@
import dalvik.system.PathClassLoader;
import dalvik.system.Zygote;
+import android.os.SELinux;
+
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -73,6 +75,7 @@
private final DataOutputStream mSocketOutStream;
private final BufferedReader mSocketReader;
private final Credentials peer;
+ private final String peerSecurityContext;
/**
* A long-lived reference to the original command socket used to launch
@@ -109,6 +112,8 @@
Log.e(TAG, "Cannot read peer credentials", ex);
throw ex;
}
+
+ peerSecurityContext = SELinux.getPeerContext(mSocket.getFileDescriptor());
}
/**
@@ -207,10 +212,11 @@
try {
parsedArgs = new Arguments(args);
- applyUidSecurityPolicy(parsedArgs, peer);
- applyRlimitSecurityPolicy(parsedArgs, peer);
- applyCapabilitiesSecurityPolicy(parsedArgs, peer);
- applyInvokeWithSecurityPolicy(parsedArgs, peer);
+ applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
+ applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
+ applyCapabilitiesSecurityPolicy(parsedArgs, peer, peerSecurityContext);
+ applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
+ applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyDebuggerSystemProperty(parsedArgs);
applyInvokeWithSystemProperty(parsedArgs);
@@ -229,7 +235,8 @@
}
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids, parsedArgs.debugFlags, rlimits);
+ parsedArgs.gids, parsedArgs.debugFlags, rlimits,
+ parsedArgs.seInfo, parsedArgs.niceName);
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (ErrnoException ex) {
@@ -352,6 +359,10 @@
long permittedCapabilities;
long effectiveCapabilities;
+ /** from --seinfo */
+ boolean seInfoSpecified;
+ String seInfo;
+
/** from all --rlimit=r,c,m */
ArrayList<int[]> rlimits;
@@ -429,6 +440,13 @@
peerWait = true;
} else if (arg.equals("--runtime-init")) {
runtimeInit = true;
+ } else if (arg.startsWith("--seinfo=")) {
+ if (seInfoSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ seInfoSpecified = true;
+ seInfo = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--capabilities=")) {
if (capabilitiesSpecified) {
throw new IllegalArgumentException(
@@ -591,7 +609,8 @@
* @param peer non-null; peer credentials
* @throws ZygoteSecurityException
*/
- private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
+ private static void applyUidSecurityPolicy(Arguments args, Credentials peer,
+ String peerSecurityContext)
throws ZygoteSecurityException {
int peerUid = peer.getUid();
@@ -624,6 +643,17 @@
}
}
+ if (args.uidSpecified || args.gidSpecified || args.gids != null) {
+ boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
+ peerSecurityContext,
+ "zygote",
+ "specifyids");
+ if (!allowed) {
+ throw new ZygoteSecurityException(
+ "Peer may not specify uid's or gid's");
+ }
+ }
+
// If not otherwise specified, uid and gid are inherited from peer
if (!args.uidSpecified) {
args.uid = peer.getUid();
@@ -664,7 +694,7 @@
* @throws ZygoteSecurityException
*/
private static void applyRlimitSecurityPolicy(
- Arguments args, Credentials peer)
+ Arguments args, Credentials peer, String peerSecurityContext)
throws ZygoteSecurityException {
int peerUid = peer.getUid();
@@ -676,6 +706,17 @@
"This UID may not specify rlimits.");
}
}
+
+ if (args.rlimits != null) {
+ boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
+ peerSecurityContext,
+ "zygote",
+ "specifyrlimits");
+ if (!allowed) {
+ throw new ZygoteSecurityException(
+ "Peer may not specify rlimits");
+ }
+ }
}
/**
@@ -689,7 +730,7 @@
* @throws ZygoteSecurityException
*/
private static void applyCapabilitiesSecurityPolicy(
- Arguments args, Credentials peer)
+ Arguments args, Credentials peer, String peerSecurityContext)
throws ZygoteSecurityException {
if (args.permittedCapabilities == 0
@@ -698,6 +739,15 @@
return;
}
+ boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
+ peerSecurityContext,
+ "zygote",
+ "specifycapabilities");
+ if (!allowed) {
+ throw new ZygoteSecurityException(
+ "Peer may not specify capabilities");
+ }
+
if (peer.getUid() == 0) {
// root may specify anything
return;
@@ -747,7 +797,8 @@
* @param peer non-null; peer credentials
* @throws ZygoteSecurityException
*/
- private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
+ private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer,
+ String peerSecurityContext)
throws ZygoteSecurityException {
int peerUid = peer.getUid();
@@ -755,6 +806,52 @@
throw new ZygoteSecurityException("Peer is not permitted to specify "
+ "an explicit invoke-with wrapper command");
}
+
+ if (args.invokeWith != null) {
+ boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
+ peerSecurityContext,
+ "zygote",
+ "specifyinvokewith");
+ if (!allowed) {
+ throw new ZygoteSecurityException("Peer is not permitted to specify "
+ + "an explicit invoke-with wrapper command");
+ }
+ }
+ }
+
+ /**
+ * Applies zygote security policy for SEAndroid information.
+ *
+ * @param args non-null; zygote spawner arguments
+ * @param peer non-null; peer credentials
+ * @throws ZygoteSecurityException
+ */
+ private static void applyseInfoSecurityPolicy(
+ Arguments args, Credentials peer, String peerSecurityContext)
+ throws ZygoteSecurityException {
+ int peerUid = peer.getUid();
+
+ if (args.seInfo == null) {
+ // nothing to check
+ return;
+ }
+
+ if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
+ // All peers with UID other than root or SYSTEM_UID
+ throw new ZygoteSecurityException(
+ "This UID may not specify SEAndroid info.");
+ }
+
+ boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
+ peerSecurityContext,
+ "zygote",
+ "specifyseinfo");
+ if (!allowed) {
+ throw new ZygoteSecurityException(
+ "Peer may not specify SEAndroid info");
+ }
+
+ return;
}
/**
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index d1c2d2e..5093b4d 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -444,6 +444,16 @@
if ((mConnection != null) && (mSrcContext != null)) {
mSrcContext.unbindService(mConnection);
}
+ try {
+ // Send the DISCONNECTED, although it may not be received
+ // but its the best we can do.
+ Message msg = Message.obtain();
+ msg.what = CMD_CHANNEL_DISCONNECTED;
+ msg.replyTo = mSrcMessenger;
+ mDstMessenger.send(msg);
+ } catch(Exception e) {
+ }
+ // Tell source we're disconnected.
if (mSrcHandler != null) {
replyDisconnected(STATUS_SUCCESSFUL);
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 1391ac3..0ea7b83 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -80,9 +80,9 @@
* and invoke <code>halting</code>. Any message subsequently received by the state
* machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
*
- * <p>If it is desirable to completely stop the state machine call <code>quit</code>. This
- * will exit the current state and its parent and then exit from the controlling thread
- * and no further messages will be processed.</p>
+ * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
+ * <code>abort</code>. These will call <code>exit</code> of the current state and its parents, call
+ * <code>onQuiting</code> and then exit Thread/Loopers.</p>
*
* <p>In addition to <code>processMessage</code> each <code>State</code> has
* an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
@@ -362,7 +362,7 @@
}
@Override
- void halting() {
+ void onHalting() {
Log.d(TAG, "halting");
synchronized (this) {
this.notifyAll();
@@ -423,10 +423,10 @@
private String mName;
/** Message.what value when quitting */
- public static final int SM_QUIT_CMD = -1;
+ private static final int SM_QUIT_CMD = -1;
/** Message.what value when initializing */
- public static final int SM_INIT_CMD = -2;
+ private static final int SM_INIT_CMD = -2;
/**
* Convenience constant that maybe returned by processMessage
@@ -443,11 +443,10 @@
public static final boolean NOT_HANDLED = false;
/**
+ * StateMachine logging record.
* {@hide}
- *
- * The information maintained for a processed message.
*/
- public static class ProcessedMessageInfo {
+ public static class LogRec {
private long mTime;
private int mWhat;
private String mInfo;
@@ -456,12 +455,13 @@
/**
* Constructor
- * @param message
+ *
+ * @param msg
* @param state that handled the message
* @param orgState is the first state the received the message but
* did not processes the message.
*/
- ProcessedMessageInfo(Message msg, String info, State state, State orgState) {
+ LogRec(Message msg, String info, State state, State orgState) {
update(msg, info, state, orgState);
}
@@ -473,7 +473,7 @@
*/
public void update(Message msg, String info, State state, State orgState) {
mTime = System.currentTimeMillis();
- mWhat = msg.what;
+ mWhat = (msg != null) ? msg.what : 0;
mInfo = info;
mState = state;
mOrgState = orgState;
@@ -517,8 +517,7 @@
/**
* @return as string
*/
- @Override
- public String toString() {
+ public String toString(StateMachine sm) {
StringBuilder sb = new StringBuilder();
sb.append("time=");
Calendar c = Calendar.getInstance();
@@ -529,10 +528,15 @@
sb.append(" orgState=");
sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
sb.append(" what=");
- sb.append(mWhat);
- sb.append("(0x");
- sb.append(Integer.toHexString(mWhat));
- sb.append(")");
+ String what = sm.getWhatToString(mWhat);
+ if (TextUtils.isEmpty(what)) {
+ sb.append(mWhat);
+ sb.append("(0x");
+ sb.append(Integer.toHexString(mWhat));
+ sb.append(")");
+ } else {
+ sb.append(what);
+ }
if ( ! TextUtils.isEmpty(mInfo)) {
sb.append(" ");
sb.append(mInfo);
@@ -542,21 +546,21 @@
}
/**
- * A list of messages recently processed by the state machine.
+ * A list of log records including messages recently processed by the state machine.
*
- * The class maintains a list of messages that have been most
+ * The class maintains a list of log records including messages
* recently processed. The list is finite and may be set in the
* constructor or by calling setSize. The public interface also
- * includes size which returns the number of recent messages,
- * count which is the number of message processed since the
- * the last setSize, get which returns a processed message and
- * add which adds a processed messaged.
+ * includes size which returns the number of recent records,
+ * count which is the number of records processed since the
+ * the last setSize, get which returns a record and
+ * add which adds a record.
*/
- private static class ProcessedMessages {
+ private static class LogRecords {
private static final int DEFAULT_SIZE = 20;
- private Vector<ProcessedMessageInfo> mMessages = new Vector<ProcessedMessageInfo>();
+ private Vector<LogRec> mLogRecords = new Vector<LogRec>();
private int mMaxSize = DEFAULT_SIZE;
private int mOldestIndex = 0;
private int mCount = 0;
@@ -564,39 +568,39 @@
/**
* private constructor use add
*/
- private ProcessedMessages() {
+ private LogRecords() {
}
/**
- * Set size of messages to maintain and clears all current messages.
+ * Set size of messages to maintain and clears all current records.
*
- * @param maxSize number of messages to maintain at anyone time.
+ * @param maxSize number of records to maintain at anyone time.
*/
- void setSize(int maxSize) {
+ synchronized void setSize(int maxSize) {
mMaxSize = maxSize;
mCount = 0;
- mMessages.clear();
+ mLogRecords.clear();
}
/**
- * @return the number of recent messages.
+ * @return the number of recent records.
*/
- int size() {
- return mMessages.size();
+ synchronized int size() {
+ return mLogRecords.size();
}
/**
- * @return the total number of messages processed since size was set.
+ * @return the total number of records processed since size was set.
*/
- int count() {
+ synchronized int count() {
return mCount;
}
/**
- * Clear the list of Processed Message Info.
+ * Clear the list of records.
*/
- void cleanup() {
- mMessages.clear();
+ synchronized void cleanup() {
+ mLogRecords.clear();
}
/**
@@ -604,7 +608,7 @@
* record and size()-1 is the newest record. If the index is to
* large null is returned.
*/
- ProcessedMessageInfo get(int index) {
+ synchronized LogRec get(int index) {
int nextIndex = mOldestIndex + index;
if (nextIndex >= mMaxSize) {
nextIndex -= mMaxSize;
@@ -612,7 +616,7 @@
if (nextIndex >= size()) {
return null;
} else {
- return mMessages.get(nextIndex);
+ return mLogRecords.get(nextIndex);
}
}
@@ -625,12 +629,12 @@
* @param orgState is the first state the received the message but
* did not processes the message.
*/
- void add(Message msg, String messageInfo, State state, State orgState) {
+ synchronized void add(Message msg, String messageInfo, State state, State orgState) {
mCount += 1;
- if (mMessages.size() < mMaxSize) {
- mMessages.add(new ProcessedMessageInfo(msg, messageInfo, state, orgState));
+ if (mLogRecords.size() < mMaxSize) {
+ mLogRecords.add(new LogRec(msg, messageInfo, state, orgState));
} else {
- ProcessedMessageInfo pmi = mMessages.get(mOldestIndex);
+ LogRec pmi = mLogRecords.get(mOldestIndex);
mOldestIndex += 1;
if (mOldestIndex >= mMaxSize) {
mOldestIndex = 0;
@@ -652,8 +656,8 @@
/** The current message */
private Message mMsg;
- /** A list of messages that this state machine has processed */
- private ProcessedMessages mProcessedMessages = new ProcessedMessages();
+ /** A list of log records including messages this state machine has processed */
+ private LogRecords mLogRecords = new LogRecords();
/** true if construction of the state machine has not been completed */
private boolean mIsConstructionCompleted;
@@ -814,15 +818,18 @@
*/
if (destState != null) {
if (destState == mQuittingState) {
+ /**
+ * Call onQuitting to let subclasses cleanup.
+ */
+ mSm.onQuitting();
cleanupAfterQuitting();
-
} else if (destState == mHaltingState) {
/**
- * Call halting() if we've transitioned to the halting
+ * Call onHalting() if we've transitioned to the halting
* state. All subsequent messages will be processed in
* in the halting state which invokes haltedProcessMessage(msg);
*/
- mSm.halting();
+ mSm.onHalting();
}
}
}
@@ -831,7 +838,6 @@
* Cleanup all the static variables and the looper after the SM has been quit.
*/
private final void cleanupAfterQuitting() {
- mSm.quitting();
if (mSm.mSmThread != null) {
// If we made the thread then quit looper which stops the thread.
getLooper().quit();
@@ -841,7 +847,7 @@
mSm.mSmHandler = null;
mSm = null;
mMsg = null;
- mProcessedMessages.cleanup();
+ mLogRecords.cleanup();
mStateStack = null;
mTempStateStack = null;
mStateInfo.clear();
@@ -892,36 +898,38 @@
if (mDbg) {
Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
}
- while (!curStateInfo.state.processMessage(msg)) {
- /**
- * Not processed
- */
- curStateInfo = curStateInfo.parentStateInfo;
- if (curStateInfo == null) {
- /**
- * No parents left so it's not handled
- */
- mSm.unhandledMessage(msg);
- if (isQuit(msg)) {
- transitionTo(mQuittingState);
- }
- break;
- }
- if (mDbg) {
- Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
- }
- }
- /**
- * Record that we processed the message
- */
- if (mSm.recordProcessedMessage(msg)) {
- if (curStateInfo != null) {
- State orgState = mStateStack[mStateStackTopIndex].state;
- mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,
- orgState);
- } else {
- mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null);
+ if (isQuit(msg)) {
+ transitionTo(mQuittingState);
+ } else {
+ while (!curStateInfo.state.processMessage(msg)) {
+ /**
+ * Not processed
+ */
+ curStateInfo = curStateInfo.parentStateInfo;
+ if (curStateInfo == null) {
+ /**
+ * No parents left so it's not handled
+ */
+ mSm.unhandledMessage(msg);
+ break;
+ }
+ if (mDbg) {
+ Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
+ }
+ }
+
+ /**
+ * Record that we processed the message
+ */
+ if (mSm.recordLogRec(msg)) {
+ if (curStateInfo != null) {
+ State orgState = mStateStack[mStateStackTopIndex].state;
+ mLogRecords.add(msg, mSm.getLogRecString(msg), curStateInfo.state,
+ orgState);
+ } else {
+ mLogRecords.add(msg, mSm.getLogRecString(msg), null, null);
+ }
}
}
}
@@ -1141,13 +1149,19 @@
mDeferredMessages.add(newMsg);
}
- /** @see StateMachine#deferMessage(Message) */
+ /** @see StateMachine#quit() */
private final void quit() {
if (mDbg) Log.d(TAG, "quit:");
sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
}
- /** @see StateMachine#isQuit(Message) */
+ /** @see StateMachine#quitNow() */
+ private final void quitNow() {
+ if (mDbg) Log.d(TAG, "abort:");
+ sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
+ }
+
+ /** Validate that the message was sent by quit or abort. */
private final boolean isQuit(Message msg) {
return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
}
@@ -1162,26 +1176,6 @@
mDbg = dbg;
}
- /** @see StateMachine#setProcessedMessagesSize(int) */
- private final void setProcessedMessagesSize(int maxSize) {
- mProcessedMessages.setSize(maxSize);
- }
-
- /** @see StateMachine#getProcessedMessagesSize() */
- private final int getProcessedMessagesSize() {
- return mProcessedMessages.size();
- }
-
- /** @see StateMachine#getProcessedMessagesCount() */
- private final int getProcessedMessagesCount() {
- return mProcessedMessages.count();
- }
-
- /** @see StateMachine#getProcessedMessageInfo(int) */
- private final ProcessedMessageInfo getProcessedMessageInfo(int index) {
- return mProcessedMessages.get(index);
- }
-
}
private SmHandler mSmHandler;
@@ -1282,8 +1276,8 @@
/**
* transition to halt state. Upon returning
* from processMessage we will exit all current
- * states, execute the halting() method and then
- * all subsequent messages haltedProcessMesage
+ * states, execute the onHalting() method and then
+ * for all subsequent messages haltedProcessMessage
* will be called.
*/
protected final void transitionToHaltingState() {
@@ -1303,7 +1297,6 @@
mSmHandler.deferMessage(msg);
}
-
/**
* Called when message wasn't handled
*
@@ -1325,7 +1318,7 @@
* transitionToHalting. All subsequent messages will invoke
* {@link StateMachine#haltedProcessMessage(Message)}
*/
- protected void halting() {
+ protected void onHalting() {
}
/**
@@ -1334,7 +1327,7 @@
* ignored. In addition, if this StateMachine created the thread, the thread will
* be stopped after this method returns.
*/
- protected void quitting() {
+ protected void onQuitting() {
}
/**
@@ -1345,33 +1338,77 @@
}
/**
- * Set size of messages to maintain and clears all current messages.
+ * Set number of log records to maintain and clears all current records.
*
* @param maxSize number of messages to maintain at anyone time.
*/
- public final void setProcessedMessagesSize(int maxSize) {
- mSmHandler.setProcessedMessagesSize(maxSize);
+ public final void setLogRecSize(int maxSize) {
+ mSmHandler.mLogRecords.setSize(maxSize);
}
/**
- * @return number of messages processed
+ * @return number of log records
*/
- public final int getProcessedMessagesSize() {
- return mSmHandler.getProcessedMessagesSize();
+ public final int getLogRecSize() {
+ return mSmHandler.mLogRecords.size();
}
/**
- * @return the total number of messages processed
+ * @return the total number of records processed
*/
- public final int getProcessedMessagesCount() {
- return mSmHandler.getProcessedMessagesCount();
+ public final int getLogRecCount() {
+ return mSmHandler.mLogRecords.count();
}
/**
- * @return a processed message information
+ * @return a log record
*/
- public final ProcessedMessageInfo getProcessedMessageInfo(int index) {
- return mSmHandler.getProcessedMessageInfo(index);
+ public final LogRec getLogRec(int index) {
+ return mSmHandler.mLogRecords.get(index);
+ }
+
+ /**
+ * Add the string to LogRecords.
+ *
+ * @param string
+ */
+ protected void addLogRec(String string) {
+ mSmHandler.mLogRecords.add(null, string, null, null);
+ }
+
+ /**
+ * Add the string and state to LogRecords
+ *
+ * @param string
+ * @param state current state
+ */
+ protected void addLogRec(String string, State state) {
+ mSmHandler.mLogRecords.add(null, string, state, null);
+ }
+
+ /**
+ * @return true if msg should be saved in the log, default is true.
+ */
+ protected boolean recordLogRec(Message msg) {
+ return true;
+ }
+
+ /**
+ * Return a string to be logged by LogRec, default
+ * is an empty string. Override if additional information is desired.
+ *
+ * @param msg that was processed
+ * @return information to be logged as a String
+ */
+ protected String getLogRecString(Message msg) {
+ return "";
+ }
+
+ /**
+ * @return the string for msg.what
+ */
+ protected String getWhatToString(int what) {
+ return null;
}
/**
@@ -1548,43 +1585,23 @@
}
/**
- * Conditionally quit the looper and stop execution.
- *
- * This sends the SM_QUIT_MSG to the state machine and
- * if not handled by any state's processMessage then the
- * state machine will be stopped and no further messages
- * will be processed.
+ * Quit the state machine after all currently queued up messages are processed.
*/
- public final void quit() {
- // mSmHandler can be null if the state machine has quit.
+ protected final void quit() {
+ // mSmHandler can be null if the state machine is already stopped.
if (mSmHandler == null) return;
mSmHandler.quit();
}
/**
- * @return ture if msg is quit
+ * Quit the state machine immediately all currently queued messages will be discarded.
*/
- protected final boolean isQuit(Message msg) {
- return mSmHandler.isQuit(msg);
- }
+ protected final void quitNow() {
+ // mSmHandler can be null if the state machine is already stopped.
+ if (mSmHandler == null) return;
- /**
- * @return true if msg should be saved in ProcessedMessage, default is true.
- */
- protected boolean recordProcessedMessage(Message msg) {
- return true;
- }
-
- /**
- * Return message info to be logged by ProcessedMessageInfo, default
- * is an empty string. Override if additional information is desired.
- *
- * @param msg that was processed
- * @return information to be logged as a String
- */
- protected String getMessageInfo(Message msg) {
- return "";
+ mSmHandler.quitNow();
}
/**
@@ -1629,9 +1646,9 @@
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(getName() + ":");
- pw.println(" total messages=" + getProcessedMessagesCount());
- for (int i=0; i < getProcessedMessagesSize(); i++) {
- pw.printf(" msg[%d]: %s\n", i, getProcessedMessageInfo(i));
+ pw.println(" total records=" + getLogRecCount());
+ for (int i=0; i < getLogRecSize(); i++) {
+ pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString(this));
pw.flush();
}
pw.println("curState=" + getCurrentState().getName());
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 73324c0..cf6029e 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -278,7 +278,7 @@
*/
public boolean showOverflowMenu() {
if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
- mPostedOpenRunnable == null) {
+ mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
mPostedOpenRunnable = new OpenOverflowRunnable(popup);
// Post this for later; we might still need a layout for the anchor to be right.
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c24f6c6..b5a2f98 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -66,6 +66,7 @@
android_os_MessageQueue.cpp \
android_os_ParcelFileDescriptor.cpp \
android_os_Parcel.cpp \
+ android_os_SELinux.cpp \
android_os_StatFs.cpp \
android_os_SystemClock.cpp \
android_os_SystemProperties.cpp \
@@ -218,6 +219,12 @@
libharfbuzz \
libz
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_SHARED_LIBRARIES += libselinux
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
endif
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 241a905..7a23747 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -133,6 +133,7 @@
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_ParcelFileDescriptor(JNIEnv *env);
+extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_StatFs(JNIEnv *env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
@@ -1146,6 +1147,7 @@
REG_JNI(register_android_os_FileUtils),
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_ParcelFileDescriptor),
+ REG_JNI(register_android_os_SELinux),
REG_JNI(register_android_os_StatFs),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
new file mode 100644
index 0000000..eb99d2b
--- /dev/null
+++ b/core/jni/android_os_SELinux.cpp
@@ -0,0 +1,502 @@
+#define LOG_TAG "SELinuxJNI"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include "jni.h"
+#include "android_runtime/AndroidRuntime.h"
+#ifdef HAVE_SELINUX
+#include "selinux/selinux.h"
+#endif
+#include <errno.h>
+
+namespace android {
+
+ static jboolean isSELinuxDisabled = true;
+
+ static void throw_NullPointerException(JNIEnv *env, const char* msg) {
+ jclass clazz;
+ clazz = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(clazz, msg);
+ }
+
+ /*
+ * Function: isSELinuxEnabled
+ * Purpose: checks whether SELinux is enabled/disbaled
+ * Parameters: none
+ * Return value : true (enabled) or false (disabled)
+ * Exceptions: none
+ */
+ static jboolean isSELinuxEnabled(JNIEnv *env, jobject classz) {
+
+ return !isSELinuxDisabled;
+ }
+
+ /*
+ * Function: isSELinuxEnforced
+ * Purpose: return the current SELinux enforce mode
+ * Parameters: none
+ * Return value: true (enforcing) or false (permissive)
+ * Exceptions: none
+ */
+ static jboolean isSELinuxEnforced(JNIEnv *env, jobject clazz) {
+#ifdef HAVE_SELINUX
+ return (security_getenforce() == 1) ? true : false;
+#else
+ return false;
+#endif
+ }
+
+ /*
+ * Function: setSELinuxEnforce
+ * Purpose: set the SE Linux enforcing mode
+ * Parameters: true (enforcing) or false (permissive)
+ * Return value: true (success) or false (fail)
+ * Exceptions: none
+ */
+ static jboolean setSELinuxEnforce(JNIEnv *env, jobject clazz, jboolean value) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return false;
+
+ int enforce = (value) ? 1 : 0;
+
+ return (security_setenforce(enforce) != -1) ? true : false;
+#else
+ return false;
+#endif
+ }
+
+ /*
+ * Function: getPeerCon
+ * Purpose: retrieves security context of peer socket
+ * Parameters:
+ * fileDescriptor: peer socket file as a FileDescriptor object
+ * Returns: jstring representing the security_context of socket or NULL if error
+ * Exceptions: NullPointerException if fileDescriptor object is NULL
+ */
+ static jstring getPeerCon(JNIEnv *env, jobject clazz, jobject fileDescriptor) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return NULL;
+
+ if (fileDescriptor == NULL) {
+ throw_NullPointerException(env, "Trying to check security context of a null peer socket.");
+ return NULL;
+ }
+
+ security_context_t context = NULL;
+ jstring securityString = NULL;
+
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+
+ if (env->ExceptionOccurred() != NULL) {
+ LOGE("There was an issue with retrieving the file descriptor");
+ goto bail;
+ }
+
+ if (getpeercon(fd, &context) == -1)
+ goto bail;
+
+ LOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context);
+
+ securityString = env->NewStringUTF(context);
+
+ bail:
+ if (context != NULL)
+ freecon(context);
+
+ return securityString;
+#else
+ return NULL;
+#endif
+ }
+
+ /*
+ * Function: setFSCreateCon
+ * Purpose: set security context used for creating a new file system object
+ * Parameters:
+ * context: security_context_t representing the new context of a file system object,
+ * set to NULL to return to the default policy behavior
+ * Returns: true on success, false on error
+ * Exception: none
+ */
+ static jboolean setFSCreateCon(JNIEnv *env, jobject clazz, jstring context) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return false;
+
+ char * securityContext = NULL;
+ const char *constant_securityContext = NULL;
+
+ if (context != NULL) {
+ constant_securityContext = env->GetStringUTFChars(context, NULL);
+
+ // GetStringUTFChars returns const char * yet setfscreatecon needs char *
+ securityContext = const_cast<char *>(constant_securityContext);
+ }
+
+ int ret;
+ if ((ret = setfscreatecon(securityContext)) == -1)
+ goto bail;
+
+ LOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context);
+
+ bail:
+ if (constant_securityContext != NULL)
+ env->ReleaseStringUTFChars(context, constant_securityContext);
+
+ return (ret == 0) ? true : false;
+#else
+ return false;
+#endif
+ }
+
+ /*
+ * Function: setFileCon
+ * Purpose: set the security context of a file object
+ * Parameters:
+ * path: the location of the file system object
+ * con: the new security context of the file system object
+ * Returns: true on success, false on error
+ * Exception: NullPointerException is thrown if either path or context strign are NULL
+ */
+ static jboolean setFileCon(JNIEnv *env, jobject clazz, jstring path, jstring con) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return false;
+
+ if (path == NULL) {
+ throw_NullPointerException(env, "Trying to change the security context of a NULL file object.");
+ return false;
+ }
+
+ if (con == NULL) {
+ throw_NullPointerException(env, "Trying to set the security context of a file object with NULL.");
+ return false;
+ }
+
+ const char *objectPath = env->GetStringUTFChars(path, NULL);
+ const char *constant_con = env->GetStringUTFChars(con, NULL);
+
+ // GetStringUTFChars returns const char * yet setfilecon needs char *
+ char *newCon = const_cast<char *>(constant_con);
+
+ int ret;
+ if ((ret = setfilecon(objectPath, newCon)) == -1)
+ goto bail;
+
+ LOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath);
+
+ bail:
+ env->ReleaseStringUTFChars(path, objectPath);
+ env->ReleaseStringUTFChars(con, constant_con);
+ return (ret == 0) ? true : false;
+#else
+ return false;
+#endif
+ }
+
+ /*
+ * Function: getFileCon
+ * Purpose: retrieves the context associated with the given path in the file system
+ * Parameters:
+ * path: given path in the file system
+ * Returns:
+ * string representing the security context string of the file object
+ * the string may be NULL if an error occured
+ * Exceptions: NullPointerException if the path object is null
+ */
+ static jstring getFileCon(JNIEnv *env, jobject clazz, jstring path) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return NULL;
+
+ if (path == NULL) {
+ throw_NullPointerException(env, "Trying to check security context of a null path.");
+ return NULL;
+ }
+
+ const char *objectPath = env->GetStringUTFChars(path, NULL);
+
+ security_context_t context = NULL;
+ jstring securityString = NULL;
+
+ if (getfilecon(objectPath, &context) == -1)
+ goto bail;
+
+ LOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath);
+
+ securityString = env->NewStringUTF(context);
+
+ bail:
+ if (context != NULL)
+ freecon(context);
+
+ env->ReleaseStringUTFChars(path, objectPath);
+
+ return securityString;
+#else
+ return NULL;
+#endif
+ }
+
+ /*
+ * Function: getCon
+ * Purpose: Get the context of the current process.
+ * Parameters: none
+ * Returns: a jstring representing the security context of the process,
+ * the jstring may be NULL if there was an error
+ * Exceptions: none
+ */
+ static jstring getCon(JNIEnv *env, jobject clazz) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return NULL;
+
+ security_context_t context = NULL;
+ jstring securityString = NULL;
+
+ if (getcon(&context) == -1)
+ goto bail;
+
+ LOGV("getCon: Successfully retrieved context '%s'", context);
+
+ securityString = env->NewStringUTF(context);
+
+ bail:
+ if (context != NULL)
+ freecon(context);
+
+ return securityString;
+#else
+ return NULL;
+#endif
+ }
+
+ /*
+ * Function: getPidCon
+ * Purpose: Get the context of a process identified by its pid
+ * Parameters:
+ * pid: a jint representing the process
+ * Returns: a jstring representing the security context of the pid,
+ * the jstring may be NULL if there was an error
+ * Exceptions: none
+ */
+ static jstring getPidCon(JNIEnv *env, jobject clazz, jint pid) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return NULL;
+
+ security_context_t context = NULL;
+ jstring securityString = NULL;
+
+ pid_t checkPid = (pid_t)pid;
+
+ if (getpidcon(checkPid, &context) == -1)
+ goto bail;
+
+ LOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid);
+
+ securityString = env->NewStringUTF(context);
+
+ bail:
+ if (context != NULL)
+ freecon(context);
+
+ return securityString;
+#else
+ return NULL;
+#endif
+ }
+
+ /*
+ * Function: getBooleanNames
+ * Purpose: Gets a list of the SELinux boolean names.
+ * Parameters: None
+ * Returns: an array of strings containing the SELinux boolean names.
+ * returns NULL string on error
+ * Exceptions: None
+ */
+ static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv clazz) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return NULL;
+
+ char **list;
+ int i, len, ret;
+ jclass stringClass;
+ jobjectArray stringArray = NULL;
+
+ if (security_get_boolean_names(&list, &len) == -1)
+ return NULL;
+
+ stringClass = env->FindClass("java/lang/String");
+ stringArray = env->NewObjectArray(len, stringClass, env->NewStringUTF(""));
+ for (i = 0; i < len; i++) {
+ jstring obj;
+ obj = env->NewStringUTF(list[i]);
+ env->SetObjectArrayElement(stringArray, i, obj);
+ env->DeleteLocalRef(obj);
+ free(list[i]);
+ }
+ free(list);
+
+ return stringArray;
+#else
+ return NULL;
+#endif
+ }
+
+ /*
+ * Function: getBooleanValue
+ * Purpose: Gets the value for the given SELinux boolean name.
+ * Parameters:
+ * String: The name of the SELinux boolean.
+ * Returns: a boolean: (true) boolean is set or (false) it is not.
+ * Exceptions: None
+ */
+ static jboolean getBooleanValue(JNIEnv *env, jobject clazz, jstring name) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return false;
+
+ const char *boolean_name;
+ int ret;
+
+ if (name == NULL)
+ return false;
+ boolean_name = env->GetStringUTFChars(name, NULL);
+ ret = security_get_boolean_active(boolean_name);
+ env->ReleaseStringUTFChars(name, boolean_name);
+ return (ret == 1) ? true : false;
+#else
+ return false;
+#endif
+ }
+
+ /*
+ * Function: setBooleanNames
+ * Purpose: Sets the value for the given SELinux boolean name.
+ * Parameters:
+ * String: The name of the SELinux boolean.
+ * Boolean: The new value of the SELinux boolean.
+ * Returns: a boolean indicating whether or not the operation succeeded.
+ * Exceptions: None
+ */
+ static jboolean setBooleanValue(JNIEnv *env, jobject clazz, jstring name, jboolean value) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return false;
+
+ const char *boolean_name = NULL;
+ int ret;
+
+ if (name == NULL)
+ return false;
+ boolean_name = env->GetStringUTFChars(name, NULL);
+ ret = security_set_boolean(boolean_name, (value) ? 1 : 0);
+ env->ReleaseStringUTFChars(name, boolean_name);
+ if (ret)
+ return false;
+
+ if (security_commit_booleans() == -1)
+ return false;
+
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ /*
+ * Function: checkSELinuxAccess
+ * Purpose: Check permissions between two security contexts.
+ * Parameters: scon: subject security context as a string
+ * tcon: object security context as a string
+ * tclass: object's security class name as a string
+ * perm: permission name as a string
+ * Returns: boolean: (true) if permission was granted, (false) otherwise
+ * Exceptions: None
+ */
+ static jboolean checkSELinuxAccess(JNIEnv *env, jobject clazz, jstring scon, jstring tcon, jstring tclass, jstring perm) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return true;
+
+ int accessGranted = -1;
+
+ const char *const_scon, *const_tcon, *mytclass, *myperm;
+ char *myscon, *mytcon;
+
+ if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL)
+ goto bail;
+
+ const_scon = env->GetStringUTFChars(scon, NULL);
+ const_tcon = env->GetStringUTFChars(tcon, NULL);
+ mytclass = env->GetStringUTFChars(tclass, NULL);
+ myperm = env->GetStringUTFChars(perm, NULL);
+
+ // selinux_check_access needs char* for some
+ myscon = const_cast<char *>(const_scon);
+ mytcon = const_cast<char *>(const_tcon);
+
+ accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL);
+
+ LOGV("selinux_check_access returned %d", accessGranted);
+
+ env->ReleaseStringUTFChars(scon, const_scon);
+ env->ReleaseStringUTFChars(tcon, const_tcon);
+ env->ReleaseStringUTFChars(tclass, mytclass);
+ env->ReleaseStringUTFChars(perm, myperm);
+
+ bail:
+ return (accessGranted == 0) ? true : false;
+
+#else
+ return true;
+#endif
+ }
+
+ /*
+ * JNI registration.
+ */
+ static JNINativeMethod method_table[] = {
+
+ /* name, signature, funcPtr */
+ { "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
+ { "getBooleanNames" , "()[Ljava/lang/String;" , (void*)getBooleanNames },
+ { "getBooleanValue" , "(Ljava/lang/String;)Z" , (void*)getBooleanValue },
+ { "getContext" , "()Ljava/lang/String;" , (void*)getCon },
+ { "getFileContext" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)getFileCon },
+ { "getPeerContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon },
+ { "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon },
+ { "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced},
+ { "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled },
+ { "setBooleanValue" , "(Ljava/lang/String;Z)Z" , (void*)setBooleanValue },
+ { "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
+ { "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
+ { "setSELinuxEnforce" , "(Z)Z" , (void*)setSELinuxEnforce},
+ };
+
+ static int log_callback(int type, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap);
+ va_end(ap);
+ return 0;
+ }
+
+ int register_android_os_SELinux(JNIEnv *env) {
+#ifdef HAVE_SELINUX
+ union selinux_callback cb;
+ cb.func_log = log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+
+ isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
+
+#endif
+ return AndroidRuntime::registerNativeMethods(
+ env, "android/os/SELinux",
+ method_table, NELEM(method_table));
+ }
+}
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
index 9c18b7e..2a2dc31 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
@@ -119,6 +119,6 @@
android:drawablePadding="8dip"
android:visibility="gone"/>
- </RelativeLayout>>
+ </RelativeLayout>
</LinearLayout>
diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml
index 21d58aa..5523807 100644
--- a/core/res/res/layout/transient_notification.xml
+++ b/core/res/res/layout/transient_notification.xml
@@ -29,6 +29,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.Small"
android:textColor="@color/bright_foreground_dark"
android:shadowColor="#BB000000"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9cd04e4..aa8b643 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2498,7 +2498,7 @@
<!-- The minimal period in milliseconds between two accessibility events of the same type
are sent to this serivce. This setting can be changed at runtime by calling
{@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
- android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->>
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="notificationTimeout" format="integer" />
<!-- Additional flags as specified in
{@link android.accessibilityservice.AccessibilityServiceInfo}.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0bae0cb..40768a0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -815,6 +815,13 @@
<java-symbol type="string" name="sms_control_title" />
<java-symbol type="string" name="sms_control_no" />
<java-symbol type="string" name="sms_control_yes" />
+ <java-symbol type="string" name="sms_premium_short_code_confirm_message" />
+ <java-symbol type="string" name="sms_premium_short_code_confirm_title" />
+ <java-symbol type="string" name="sms_short_code_confirm_allow" />
+ <java-symbol type="string" name="sms_short_code_confirm_deny" />
+ <java-symbol type="string" name="sms_short_code_confirm_message" />
+ <java-symbol type="string" name="sms_short_code_confirm_report" />
+ <java-symbol type="string" name="sms_short_code_confirm_title" />
<java-symbol type="string" name="submit" />
<java-symbol type="string" name="sync_binding_label" />
<java-symbol type="string" name="sync_do_nothing" />
@@ -1139,6 +1146,7 @@
<java-symbol type="xml" name="password_kbd_symbols_shift" />
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="time_zones_by_country" />
+ <java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="raw" name="accessibility_gestures" />
<java-symbol type="raw" name="incognito_mode_start_page" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 325b6fe..3582768 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2923,6 +2923,22 @@
<!-- See SMS_DIALOG. This is a button choice to disallow sending the SMSes. [CHAR LIMIT=30] -->
<string name="sms_control_no">Deny</string>
+ <!-- SMS short code verification dialog. --> <skip />
+ <!-- The dialog title for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
+ <string name="sms_short_code_confirm_title">Send SMS to short code?</string>
+ <!-- The dialog title for the SMS premium short code confirmation dialog. [CHAR LIMIT=30] -->
+ <string name="sms_premium_short_code_confirm_title">Send premium SMS?</string>
+ <!-- The message text for the SMS short code confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="sms_short_code_confirm_message"><b><xliff:g id="app_name">%1$s</xliff:g></b> would like to send a text message to <b><xliff:g id="dest_address">%2$s</xliff:g></b>, which appears to be an SMS short code.<p>Sending text messages to some short codes may cause your mobile account to be billed for premium services.<p>Do you want to allow this app to send the message?</string>
+ <!-- The message text for the SMS short code confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="sms_premium_short_code_confirm_message"><b><xliff:g id="app_name">%1$s</xliff:g></b> would like to send a text message to <b><xliff:g id="dest_address">%2$s</xliff:g></b>, which is a premium SMS short code.<p><b>Sending a message to this destination will cause your mobile account to be billed for premium services.</b><p>Do you want to allow this app to send the message?</string>
+ <!-- Text of the approval button for the SMS short code confirmation dialog. [CHAR LIMIT=50] -->
+ <string name="sms_short_code_confirm_allow">Send message</string>
+ <!-- Text of the cancel button for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
+ <string name="sms_short_code_confirm_deny">Don\'t send</string>
+ <!-- Text of the button for the SMS short code confirmation dialog to report a malicious app. [CHAR LIMIT=30] -->
+ <string name="sms_short_code_confirm_report">Report malicious app</string>
+
<!-- SIM swap and device reboot Dialog --> <skip />
<!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. -->
<string name="sim_removed_title">SIM card removed</string>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
new file mode 100644
index 0000000..8b395af
--- /dev/null
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Regex patterns for SMS short codes by country. -->
+<shortcodes>
+
+ <!-- The country attribute is the ISO country code of the user's account (from SIM card or NV).
+ The pattern attribute is a regex that matches all SMS short codes for the country.
+ The premium attribute is a regex that matches premium rate SMS short codes.
+ The free attribute matches short codes that we know will not cost the user, such as
+ emergency numbers. The standard attribute matches short codes that are billed at the
+ standard SMS rate. The user is warned when the destination phone number matches the
+ "pattern" or "premium" regexes, and does not match the "free" or "standard" regexes. -->
+
+ <!-- Harmonised European Short Codes are 6 digit numbers starting with 116 (free helplines).
+ Premium patterns include short codes from: http://aonebill.com/coverage&tariffs
+ and http://mobilcent.com/info-worldwide.asp and extracted from:
+ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
+
+ <!-- Albania: 5 digits, known short codes listed -->
+ <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
+
+ <!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
+ <shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
+
+ <!-- Austria: 10 digits, premium prefix 09xx, plus EU -->
+ <shortcode country="at" pattern="11\\d{4}" premium="09.*" free="116\\d{3}" />
+
+ <!-- Australia: 6 or 8 digits starting with "19" -->
+ <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882" />
+
+ <!-- Azerbaijan: 4-5 digits, known premium codes listed -->
+ <shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
+
+ <!-- Belgium: 4 digits, plus EU: http://www.mobileweb.be/en/mobileweb/sms-numberplan.asp -->
+ <shortcode country="be" premium="\\d{4}" free="8\\d{3}|116\\d{3}" />
+
+ <!-- Bulgaria: 4-5 digits, plus EU -->
+ <shortcode country="bg" pattern="\\d{4,5}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}" />
+
+ <!-- Belarus: 4 digits -->
+ <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
+
+ <!-- Canada: 5-6 digits -->
+ <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188" />
+
+ <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
+ <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111" />
+
+ <!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
+ http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
+ <shortcode country="cn" premium="1066.*" free="1065.*" />
+
+ <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
+ <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
+
+ <!-- Czech Republic: 7-8 digits, starting with 9, plus EU:
+ http://www.o2.cz/osobni/en/services-by-alphabet/91670-premium_sms.html -->
+ <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
+
+ <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
+ <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}" />
+
+ <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
+ <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
+
+ <!-- Estonia: short codes 3-5 digits starting with 1, plus premium 7 digit numbers starting with 90, plus EU.
+ http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
+ <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}" />
+
+ <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
+ http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
+ <shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}" />
+
+ <!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
+ <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}" />
+
+ <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
+ http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements -->
+ <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}" />
+
+ <!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
+ http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf -->
+ <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}" />
+
+ <!-- Georgia: 4 digits, known premium codes listed -->
+ <shortcode country="ge" pattern="\\d{4}" premium="801[234]|888[239]" />
+
+ <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
+ <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}" />
+
+ <!-- Hungary: 4 or 10 digits starting with 1 or 0, plus EU:
+ http://clients.txtnation.com/entries/209633-hungary-premium-sms-short-code-regulations -->
+ <shortcode country="hu" pattern="[01](?:\\d{3}|\\d{9})" premium="0691227910|1784" free="116\\d{3}" />
+
+ <!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
+ http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
+ <shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
+
+ <!-- Israel: 4 digits, known premium codes listed -->
+ <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
+
+ <!-- Italy: 5 digits (premium=4xxxx), plus EU:
+ http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
+ <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}" />
+
+ <!-- Kyrgyzstan: 4 digits, known premium codes listed -->
+ <shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
+
+ <!-- Kazakhstan: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-kazakhstan/ -->
+ <shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
+
+ <!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
+ <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}" />
+
+ <!-- Luxembourg: 5 digits, 6xxxx, plus EU:
+ http://www.luxgsm.lu/assets/files/filepage/file_1253803400.pdf -->
+ <shortcode country="lu" premium="6\\d{4}" free="116\\d{3}" />
+
+ <!-- Latvia: 4 digits, known premium codes listed, plus EU -->
+ <shortcode country="lv" pattern="\\d{4}" premium="18(?:19|63|7[1-4])" free="116\\d{3}" />
+
+ <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" />
+
+ <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
+ <shortcode country="my" pattern="\\d{5}" premium="32298|33776" />
+
+ <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
+ <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}" />
+
+ <!-- Norway: 4-5 digits (not confirmed), known premium codes listed -->
+ <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" />
+
+ <!-- New Zealand: 3-4 digits, known premium codes listed -->
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995" />
+
+ <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
+ <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}" />
+
+ <!-- Portugal: 5 digits, plus EU:
+ http://clients.txtnation.com/entries/158326-portugal-premium-sms-short-code-regulations -->
+ <shortcode country="pt" premium="6[1289]\\d{3}" free="116\\d{3}" />
+
+ <!-- Romania: 4 digits, plus EU: http://www.simplus.ro/en/resources/glossary-of-terms/ -->
+ <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}" />
+
+ <!-- Russia: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-russia/ -->
+ <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)" />
+
+ <!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
+ <shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
+
+ <!-- Singapore: 5 digits: http://clients.txtnation.com/entries/306442-singapore-premium-sms-short-code-requirements
+ Free government directory info at 74688: http://app.sgdi.gov.sg/sms_help.asp -->
+ <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688" />
+
+ <!-- Slovenia: 4 digits (premium=3xxx, 6xxx, 8xxx), plus EU: http://www.cmtelecom.com/premium-sms/slovenia -->
+ <shortcode country="si" pattern="\\d{4}" premium="[368]\\d{3}" free="116\\d{3}" />
+
+ <!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
+ <shortcode country="sk" premium="\\d{4}" free="116\\d{3}" />
+
+ <!-- Tajikistan: 4 digits, known premium codes listed -->
+ <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
+
+ <!-- Ukraine: 4 digits, known premium codes listed -->
+ <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
+
+ <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm) -->
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" />
+
+</shortcodes>
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.mk b/core/tests/coretests/apks/install_complete_package_info/Android.mk
new file mode 100644
index 0000000..1edccb4
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_complete_package_info
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
new file mode 100644
index 0000000..4b01736
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_complete_package_info">
+
+<!--
+ This manifest declares at least one of each of the components that
+ can be retrieved by PackageManager.getInstalledPackages.
+ All the implementing classes are empty implementations
+-->
+
+ <uses-feature
+ android:name="com.android.frameworks.coretests.nonexistent" />
+ <uses-configuration
+ android:reqFiveWayNav="false" />
+
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.frameworks.coretests"
+ android:label="Frameworks Core Tests" />
+
+ <permission
+ android:label="test permission"
+ android:name="test_permission"
+ android:protectionLevel="normal" />
+
+ <application
+ android:hasCode="true">
+ <activity
+ android:name="com.android.frameworks.coretests.TestActivity">
+ </activity>
+ <provider
+ android:name="com.android.frameworks.coretests.TestProvider"
+ android:authorities="com.android.frameworks.coretests.testprovider" />
+ <receiver
+ android:name="com.android.frameworks.coretests.TestReceiver" />
+ <service
+ android:name="com.android.frameworks.coretests.TestService" />
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java
new file mode 100644
index 0000000..10d0551
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java
@@ -0,0 +1,24 @@
+/*
+ * 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 com.android.frameworks.coretests;
+
+import android.app.Activity;
+
+public class TestActivity extends Activity {
+
+}
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java
new file mode 100644
index 0000000..59f9f10
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.frameworks.coretests;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class TestProvider extends ContentProvider {
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java
new file mode 100644
index 0000000..21f6263
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.frameworks.coretests;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class TestReceiver extends ContentProvider {
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java
new file mode 100644
index 0000000..b330e75
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.android.frameworks.coretests;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class TestService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 77e4986..6e1b9d6 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -50,6 +51,8 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
+
public class PackageManagerTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG="PackageManagerTests";
@@ -3162,6 +3165,92 @@
assertNotNull("Verifier device identity should not be null", id);
}
+ public void testGetInstalledPackages() {
+ List<PackageInfo> packages = getPm().getInstalledPackages(0);
+ assertNotNull("installed packages cannot be null", packages);
+ assertTrue("installed packages cannot be empty", packages.size() > 0);
+ }
+
+ public void testGetUnInstalledPackages() {
+ List<PackageInfo> packages = getPm().getInstalledPackages(
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ assertNotNull("installed packages cannot be null", packages);
+ assertTrue("installed packages cannot be empty", packages.size() > 0);
+ }
+
+ /**
+ * Test that getInstalledPackages returns all the data specified in
+ * flags.
+ */
+ public void testGetInstalledPackagesAll() {
+ int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
+ | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
+ | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
+ | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
+ | PackageManager.GET_SIGNATURES | PackageManager.GET_UNINSTALLED_PACKAGES;
+
+ List<PackageInfo> packages = getPm().getInstalledPackages(flags);
+ assertNotNull("installed packages cannot be null", packages);
+ assertTrue("installed packages cannot be empty", packages.size() > 0);
+
+ PackageInfo packageInfo = null;
+
+ // Find the package with all components specified in the AndroidManifest
+ // to ensure no null values
+ for (PackageInfo pi : packages) {
+ if ("com.android.frameworks.coretests.install_complete_package_info"
+ .equals(pi.packageName)) {
+ packageInfo = pi;
+ break;
+ }
+ }
+ assertNotNull("activities should not be null", packageInfo.activities);
+ assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
+ assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
+ assertNotNull("permissions should not be null", packageInfo.permissions);
+ assertNotNull("providers should not be null", packageInfo.providers);
+ assertNotNull("receivers should not be null", packageInfo.receivers);
+ assertNotNull("services should not be null", packageInfo.services);
+ assertNotNull("signatures should not be null", packageInfo.signatures);
+ }
+
+ /**
+ * Test that getInstalledPackages returns all the data specified in
+ * flags when the GET_UNINSTALLED_PACKAGES flag is set.
+ */
+ public void testGetUnInstalledPackagesAll() {
+ int flags = PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
+ | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
+ | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
+ | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
+ | PackageManager.GET_SIGNATURES | PackageManager.GET_UNINSTALLED_PACKAGES;
+
+ List<PackageInfo> packages = getPm().getInstalledPackages(flags);
+ assertNotNull("installed packages cannot be null", packages);
+ assertTrue("installed packages cannot be empty", packages.size() > 0);
+
+ PackageInfo packageInfo = null;
+
+ // Find the package with all components specified in the AndroidManifest
+ // to ensure no null values
+ for (PackageInfo pi : packages) {
+ if ("com.android.frameworks.coretests.install_complete_package_info"
+ .equals(pi.packageName)) {
+ packageInfo = pi;
+ break;
+ }
+ }
+ assertNotNull("activities should not be null", packageInfo.activities);
+ assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
+ assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
+ assertNotNull("permissions should not be null", packageInfo.permissions);
+ assertNotNull("providers should not be null", packageInfo.providers);
+ assertNotNull("receivers should not be null", packageInfo.receivers);
+ assertNotNull("services should not be null", packageInfo.services);
+ assertNotNull("signatures should not be null", packageInfo.signatures);
+ }
+
/*---------- Recommended install location tests ----*/
/*
* TODO's
diff --git a/core/tests/coretests/src/android/os/SELinuxTest.java b/core/tests/coretests/src/android/os/SELinuxTest.java
new file mode 100644
index 0000000..9b63a6b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/SELinuxTest.java
@@ -0,0 +1,45 @@
+package android.os;
+
+import android.os.Process;
+import android.os.SELinux;
+import android.test.AndroidTestCase;
+import static junit.framework.Assert.assertEquals;
+
+public class SELinuxTest extends AndroidTestCase {
+
+ public void testgetFileCon() {
+ if(SELinux.isSELinuxEnabled() == false)
+ return;
+
+ String ctx = SELinux.getFileContext("/system/bin/toolbox");
+ assertEquals(ctx, "u:object_r:system_file:s0");
+ }
+
+ public void testgetCon() {
+ if(SELinux.isSELinuxEnabled() == false)
+ return;
+
+ String mycon = SELinux.getContext();
+ assertEquals(mycon, "u:r:untrusted_app:s0:c33");
+ }
+
+ public void testgetPidCon() {
+ if(SELinux.isSELinuxEnabled() == false)
+ return;
+
+ String mycon = SELinux.getPidContext(Process.myPid());
+ assertEquals(mycon, "u:r:untrusted_app:s0:c33");
+ }
+
+ public void testcheckSELinuxAccess() {
+ if(SELinux.isSELinuxEnabled() == false)
+ return;
+
+ String mycon = SELinux.getContext();
+ boolean ret;
+ ret = SELinux.checkSELinuxAccess(mycon, mycon, "process", "fork");
+ assertEquals(ret,"true");
+ ret = SELinux.checkSELinuxAccess(mycon, mycon, "memprotect", "mmap_zero");
+ assertEquals(ret,"true");
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
index ab6b2b6..418bbd6 100644
--- a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
@@ -24,7 +24,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.internal.util.StateMachine.ProcessedMessageInfo;
+import com.android.internal.util.StateMachine.LogRec;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -36,6 +36,10 @@
* Test for StateMachine.
*/
public class StateMachineTest extends TestCase {
+ private static final String ENTER = "enter";
+ private static final String EXIT = "exit";
+ private static final String ON_QUITTING = "ON_QUITTING";
+
private static final int TEST_CMD_1 = 1;
private static final int TEST_CMD_2 = 2;
private static final int TEST_CMD_3 = 3;
@@ -47,11 +51,18 @@
private static final boolean WAIT_FOR_DEBUGGER = false;
private static final String TAG = "StateMachineTest";
+ private void sleep(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch(InterruptedException e) {
+ }
+ }
+
/**
- * Tests that we can quit the state machine.
+ * Tests {@link StateMachine#quit()}.
*/
class StateMachineQuitTest extends StateMachine {
- private int mQuitCount = 0;
+ Object mWaitUntilTestDone = new Object();
StateMachineQuitTest(String name) {
super(name);
@@ -65,29 +76,45 @@
setInitialState(mS1);
}
- class S1 extends State {
- @Override
- public boolean processMessage(Message message) {
- if (isQuit(message)) {
- mQuitCount += 1;
- if (mQuitCount > 2) {
- // Returning NOT_HANDLED to actually quit
- return NOT_HANDLED;
- } else {
- // Do NOT quit
- return HANDLED;
- }
- } else {
- // All other message are handled
- return HANDLED;
+ @Override
+ public void onQuitting() {
+ Log.d(TAG, "onQuitting");
+ addLogRec(ON_QUITTING);
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+
+ // Don't leave onQuitting before the test is done as everything is cleared
+ // including the log records.
+ synchronized (mWaitUntilTestDone) {
+ try {
+ mWaitUntilTestDone.wait();
+ } catch(InterruptedException e) {
}
}
}
- @Override
- protected void quitting() {
- synchronized (mThisSm) {
- mThisSm.notifyAll();
+ class S1 extends State {
+ public void exit() {
+ Log.d(TAG, "S1.exit");
+ addLogRec(EXIT, mS1);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ // Sleep and assume the other messages will be queued up.
+ case TEST_CMD_1: {
+ Log.d(TAG, "TEST_CMD_1");
+ sleep(500);
+ quit();
+ break;
+ }
+ default: {
+ Log.d(TAG, "default what=" + message.what);
+ break;
+ }
+ }
+ return HANDLED;
}
}
@@ -96,62 +123,167 @@
}
@SmallTest
- public void testStateMachineQuitTest() throws Exception {
+ public void testStateMachineQuit() throws Exception {
if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest");
smQuitTest.start();
- if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuitTest E");
+ if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit E");
synchronized (smQuitTest) {
- // Send 6 messages
+
+ // Send 6 message we'll quit on the first but all 6 should be processed before quitting.
for (int i = 1; i <= 6; i++) {
- smQuitTest.sendMessage(i);
+ smQuitTest.sendMessage(smQuitTest.obtainMessage(i));
}
- // First two are ignored
- smQuitTest.quit();
- smQuitTest.quit();
-
- // Now we will quit
- smQuitTest.quit();
-
try {
// wait for the messages to be handled
smQuitTest.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachineQuitTest: exception while waiting " + e.getMessage());
+ Log.e(TAG, "testStateMachineQuit: exception while waiting " + e.getMessage());
}
}
- assertTrue(smQuitTest.getProcessedMessagesCount() == 9);
+ assertEquals(8, smQuitTest.getLogRecCount());
- ProcessedMessageInfo pmi;
+ LogRec lr;
- // The first two message didn't quit and were handled by mS1
- pmi = smQuitTest.getProcessedMessageInfo(6);
- assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
- assertEquals(smQuitTest.mS1, pmi.getState());
- assertEquals(smQuitTest.mS1, pmi.getOriginalState());
+ for (int i = 0; i < 6; i++) {
+ lr = smQuitTest.getLogRec(i);
+ assertEquals(i+1, lr.getWhat());
+ assertEquals(smQuitTest.mS1, lr.getState());
+ assertEquals(smQuitTest.mS1, lr.getOriginalState());
+ }
+ lr = smQuitTest.getLogRec(6);
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smQuitTest.mS1, lr.getState());
- pmi = smQuitTest.getProcessedMessageInfo(7);
- assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
- assertEquals(smQuitTest.mS1, pmi.getState());
- assertEquals(smQuitTest.mS1, pmi.getOriginalState());
+ lr = smQuitTest.getLogRec(7);
+ assertEquals(ON_QUITTING, lr.getInfo());
- // The last message was never handled so the states are null
- pmi = smQuitTest.getProcessedMessageInfo(8);
- assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
- assertEquals(null, pmi.getState());
- assertEquals(null, pmi.getOriginalState());
+ synchronized (smQuitTest.mWaitUntilTestDone) {
+ smQuitTest.mWaitUntilTestDone.notifyAll();
+ }
+ if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit X");
+ }
- if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuitTest X");
+ /**
+ * Tests {@link StateMachine#quitNow()}
+ */
+ class StateMachineQuitNowTest extends StateMachine {
+ Object mWaitUntilTestDone = new Object();
+
+ StateMachineQuitNowTest(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ @Override
+ public void onQuitting() {
+ Log.d(TAG, "onQuitting");
+ addLogRec(ON_QUITTING);
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+
+ // Don't leave onQuitting before the test is done as everything is cleared
+ // including the log records.
+ synchronized (mWaitUntilTestDone) {
+ try {
+ mWaitUntilTestDone.wait();
+ } catch(InterruptedException e) {
+ }
+ }
+ }
+
+ class S1 extends State {
+ public void exit() {
+ Log.d(TAG, "S1.exit");
+ addLogRec(EXIT, mS1);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ // Sleep and assume the other messages will be queued up.
+ case TEST_CMD_1: {
+ Log.d(TAG, "TEST_CMD_1");
+ sleep(500);
+ quitNow();
+ break;
+ }
+ default: {
+ Log.d(TAG, "default what=" + message.what);
+ break;
+ }
+ }
+ return HANDLED;
+ }
+ }
+
+ private StateMachineQuitNowTest mThisSm;
+ private S1 mS1 = new S1();
+ }
+
+ @SmallTest
+ public void testStateMachineQuitNow() throws Exception {
+ if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+ StateMachineQuitNowTest smQuitNowTest = new StateMachineQuitNowTest("smQuitNowTest");
+ smQuitNowTest.start();
+ if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow E");
+
+ synchronized (smQuitNowTest) {
+
+ // Send 6 messages but we'll QuitNow on the first so even though
+ // we send 6 only one will be processed.
+ for (int i = 1; i <= 6; i++) {
+ smQuitNowTest.sendMessage(smQuitNowTest.obtainMessage(i));
+ }
+
+ try {
+ // wait for the messages to be handled
+ smQuitNowTest.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachineQuitNow: exception while waiting " + e.getMessage());
+ }
+ }
+
+ // Only three records because we executed quitNow.
+ assertEquals(3, smQuitNowTest.getLogRecCount());
+
+ LogRec lr;
+
+ lr = smQuitNowTest.getLogRec(0);
+ assertEquals(1, lr.getWhat());
+ assertEquals(smQuitNowTest.mS1, lr.getState());
+ assertEquals(smQuitNowTest.mS1, lr.getOriginalState());
+
+ lr = smQuitNowTest.getLogRec(1);
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smQuitNowTest.mS1, lr.getState());
+
+ lr = smQuitNowTest.getLogRec(2);
+ assertEquals(ON_QUITTING, lr.getInfo());
+
+ synchronized (smQuitNowTest.mWaitUntilTestDone) {
+ smQuitNowTest.mWaitUntilTestDone.notifyAll();
+ }
+ if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow X");
}
/**
* Test enter/exit can use transitionTo
*/
class StateMachineEnterExitTransitionToTest extends StateMachine {
+
StateMachineEnterExitTransitionToTest(String name) {
super(name);
mThisSm = this;
@@ -170,20 +302,15 @@
class S1 extends State {
@Override
public void enter() {
- // Test that message is HSM_INIT_CMD
- assertEquals(SM_INIT_CMD, getCurrentMessage().what);
-
- // Test that a transition in enter and the initial state works
- mS1EnterCount += 1;
+ // Test transitions in enter on the initial state work
+ addLogRec(ENTER, mS1);
transitionTo(mS2);
Log.d(TAG, "S1.enter");
}
@Override
public void exit() {
// Test that message is HSM_INIT_CMD
- assertEquals(SM_INIT_CMD, getCurrentMessage().what);
-
- mS1ExitCount += 1;
+ addLogRec(EXIT, mS1);
Log.d(TAG, "S1.exit");
}
}
@@ -191,19 +318,15 @@
class S2 extends State {
@Override
public void enter() {
- // Test that message is HSM_INIT_CMD
- assertEquals(SM_INIT_CMD, getCurrentMessage().what);
-
- mS2EnterCount += 1;
+ addLogRec(ENTER, mS2);
Log.d(TAG, "S2.enter");
}
@Override
public void exit() {
- // Test that message is TEST_CMD_1
+ addLogRec(EXIT, mS2);
assertEquals(TEST_CMD_1, getCurrentMessage().what);
// Test transition in exit work
- mS2ExitCount += 1;
transitionTo(mS4);
Log.d(TAG, "S2.exit");
}
@@ -220,36 +343,33 @@
class S3 extends State {
@Override
public void enter() {
- // Test that we can do halting in an enter/exit
- transitionToHaltingState();
- mS3EnterCount += 1;
+ addLogRec(ENTER, mS3);
Log.d(TAG, "S3.enter");
}
@Override
public void exit() {
- mS3ExitCount += 1;
+ addLogRec(EXIT, mS3);
Log.d(TAG, "S3.exit");
}
}
-
class S4 extends State {
@Override
public void enter() {
+ addLogRec(ENTER, mS4);
// Test that we can do halting in an enter/exit
transitionToHaltingState();
- mS4EnterCount += 1;
Log.d(TAG, "S4.enter");
}
@Override
public void exit() {
- mS4ExitCount += 1;
+ addLogRec(EXIT, mS4);
Log.d(TAG, "S4.exit");
}
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -260,14 +380,6 @@
private S2 mS2 = new S2();
private S3 mS3 = new S3();
private S4 mS4 = new S4();
- private int mS1EnterCount = 0;
- private int mS1ExitCount = 0;
- private int mS2EnterCount = 0;
- private int mS2ExitCount = 0;
- private int mS3EnterCount = 0;
- private int mS3ExitCount = 0;
- private int mS4EnterCount = 0;
- private int mS4ExitCount = 0;
}
@SmallTest
@@ -293,24 +405,46 @@
}
}
- assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);
+ assertEquals(smEnterExitTranstionToTest.getLogRecCount(), 9);
- ProcessedMessageInfo pmi;
+ LogRec lr;
- // Message should be handled by mS2.
- pmi = smEnterExitTranstionToTest.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
- assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());
+ lr = smEnterExitTranstionToTest.getLogRec(0);
+ assertEquals(ENTER, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS1, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS1EnterCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS1ExitCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS2EnterCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS2ExitCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
- assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
+ lr = smEnterExitTranstionToTest.getLogRec(1);
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS1, lr.getState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(2);
+ assertEquals(ENTER, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(3);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
+ assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(4);
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(5);
+ assertEquals(ENTER, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(6);
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(7);
+ assertEquals(ENTER, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
+
+ lr = smEnterExitTranstionToTest.getLogRec(8);
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
if (smEnterExitTranstionToTest.isDbg()) {
Log.d(TAG, "testStateMachineEnterExitTransitionToTest X");
@@ -325,7 +459,7 @@
super(name);
mThisSm = this;
setDbg(DBG);
- setProcessedMessagesSize(3);
+ setLogRecSize(3);
// Setup state machine with 1 state
addState(mS1);
@@ -345,7 +479,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -377,24 +511,24 @@
}
}
- assertTrue(sm0.getProcessedMessagesCount() == 6);
- assertTrue(sm0.getProcessedMessagesSize() == 3);
+ assertEquals(6, sm0.getLogRecCount());
+ assertEquals(3, sm0.getLogRecSize());
- ProcessedMessageInfo pmi;
- pmi = sm0.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_4, pmi.getWhat());
- assertEquals(sm0.mS1, pmi.getState());
- assertEquals(sm0.mS1, pmi.getOriginalState());
+ LogRec lr;
+ lr = sm0.getLogRec(0);
+ assertEquals(TEST_CMD_4, lr.getWhat());
+ assertEquals(sm0.mS1, lr.getState());
+ assertEquals(sm0.mS1, lr.getOriginalState());
- pmi = sm0.getProcessedMessageInfo(1);
- assertEquals(TEST_CMD_5, pmi.getWhat());
- assertEquals(sm0.mS1, pmi.getState());
- assertEquals(sm0.mS1, pmi.getOriginalState());
+ lr = sm0.getLogRec(1);
+ assertEquals(TEST_CMD_5, lr.getWhat());
+ assertEquals(sm0.mS1, lr.getState());
+ assertEquals(sm0.mS1, lr.getOriginalState());
- pmi = sm0.getProcessedMessageInfo(2);
- assertEquals(TEST_CMD_6, pmi.getWhat());
- assertEquals(sm0.mS1, pmi.getState());
- assertEquals(sm0.mS1, pmi.getOriginalState());
+ lr = sm0.getLogRec(2);
+ assertEquals(TEST_CMD_6, lr.getWhat());
+ assertEquals(sm0.mS1, lr.getState());
+ assertEquals(sm0.mS1, lr.getOriginalState());
if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X");
}
@@ -444,7 +578,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -479,18 +613,18 @@
assertEquals(2, sm1.mEnterCount);
assertEquals(2, sm1.mExitCount);
- assertTrue(sm1.getProcessedMessagesSize() == 2);
+ assertEquals(2, sm1.getLogRecSize());
- ProcessedMessageInfo pmi;
- pmi = sm1.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(sm1.mS1, pmi.getState());
- assertEquals(sm1.mS1, pmi.getOriginalState());
+ LogRec lr;
+ lr = sm1.getLogRec(0);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(sm1.mS1, lr.getState());
+ assertEquals(sm1.mS1, lr.getOriginalState());
- pmi = sm1.getProcessedMessageInfo(1);
- assertEquals(TEST_CMD_2, pmi.getWhat());
- assertEquals(sm1.mS1, pmi.getState());
- assertEquals(sm1.mS1, pmi.getOriginalState());
+ lr = sm1.getLogRec(1);
+ assertEquals(TEST_CMD_2, lr.getWhat());
+ assertEquals(sm1.mS1, lr.getState());
+ assertEquals(sm1.mS1, lr.getOriginalState());
assertEquals(2, sm1.mEnterCount);
assertEquals(2, sm1.mExitCount);
@@ -550,7 +684,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -583,24 +717,24 @@
}
}
- assertTrue(sm2.getProcessedMessagesSize() == 4);
+ assertEquals(4, sm2.getLogRecSize());
- ProcessedMessageInfo pmi;
- pmi = sm2.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(sm2.mS1, pmi.getState());
+ LogRec lr;
+ lr = sm2.getLogRec(0);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(sm2.mS1, lr.getState());
- pmi = sm2.getProcessedMessageInfo(1);
- assertEquals(TEST_CMD_2, pmi.getWhat());
- assertEquals(sm2.mS1, pmi.getState());
+ lr = sm2.getLogRec(1);
+ assertEquals(TEST_CMD_2, lr.getWhat());
+ assertEquals(sm2.mS1, lr.getState());
- pmi = sm2.getProcessedMessageInfo(2);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(sm2.mS2, pmi.getState());
+ lr = sm2.getLogRec(2);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(sm2.mS2, lr.getState());
- pmi = sm2.getProcessedMessageInfo(3);
- assertEquals(TEST_CMD_2, pmi.getWhat());
- assertEquals(sm2.mS2, pmi.getState());
+ lr = sm2.getLogRec(3);
+ assertEquals(TEST_CMD_2, lr.getWhat());
+ assertEquals(sm2.mS2, lr.getState());
assertTrue(sm2.mDidEnter);
assertTrue(sm2.mDidExit);
@@ -647,7 +781,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -677,18 +811,18 @@
}
}
- assertTrue(sm3.getProcessedMessagesSize() == 2);
+ assertEquals(2, sm3.getLogRecSize());
- ProcessedMessageInfo pmi;
- pmi = sm3.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(sm3.mParentState, pmi.getState());
- assertEquals(sm3.mChildState, pmi.getOriginalState());
+ LogRec lr;
+ lr = sm3.getLogRec(0);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(sm3.mParentState, lr.getState());
+ assertEquals(sm3.mChildState, lr.getOriginalState());
- pmi = sm3.getProcessedMessageInfo(1);
- assertEquals(TEST_CMD_2, pmi.getWhat());
- assertEquals(sm3.mParentState, pmi.getState());
- assertEquals(sm3.mChildState, pmi.getOriginalState());
+ lr = sm3.getLogRec(1);
+ assertEquals(TEST_CMD_2, lr.getWhat());
+ assertEquals(sm3.mParentState, lr.getState());
+ assertEquals(sm3.mChildState, lr.getOriginalState());
if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X");
}
@@ -742,7 +876,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -774,18 +908,18 @@
}
- assertTrue(sm4.getProcessedMessagesSize() == 2);
+ assertEquals(2, sm4.getLogRecSize());
- ProcessedMessageInfo pmi;
- pmi = sm4.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(sm4.mChildState1, pmi.getState());
- assertEquals(sm4.mChildState1, pmi.getOriginalState());
+ LogRec lr;
+ lr = sm4.getLogRec(0);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(sm4.mChildState1, lr.getState());
+ assertEquals(sm4.mChildState1, lr.getOriginalState());
- pmi = sm4.getProcessedMessageInfo(1);
- assertEquals(TEST_CMD_2, pmi.getWhat());
- assertEquals(sm4.mParentState, pmi.getState());
- assertEquals(sm4.mChildState2, pmi.getOriginalState());
+ lr = sm4.getLogRec(1);
+ assertEquals(TEST_CMD_2, lr.getWhat());
+ assertEquals(sm4.mParentState, lr.getState());
+ assertEquals(sm4.mChildState2, lr.getOriginalState());
if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X");
}
@@ -1018,7 +1152,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -1073,7 +1207,7 @@
}
- assertTrue(sm5.getProcessedMessagesSize() == 6);
+ assertEquals(6, sm5.getLogRecSize());
assertEquals(1, sm5.mParentState1EnterCount);
assertEquals(1, sm5.mParentState1ExitCount);
@@ -1090,36 +1224,36 @@
assertEquals(1, sm5.mChildState5EnterCount);
assertEquals(1, sm5.mChildState5ExitCount);
- ProcessedMessageInfo pmi;
- pmi = sm5.getProcessedMessageInfo(0);
- assertEquals(TEST_CMD_1, pmi.getWhat());
- assertEquals(sm5.mChildState1, pmi.getState());
- assertEquals(sm5.mChildState1, pmi.getOriginalState());
+ LogRec lr;
+ lr = sm5.getLogRec(0);
+ assertEquals(TEST_CMD_1, lr.getWhat());
+ assertEquals(sm5.mChildState1, lr.getState());
+ assertEquals(sm5.mChildState1, lr.getOriginalState());
- pmi = sm5.getProcessedMessageInfo(1);
- assertEquals(TEST_CMD_2, pmi.getWhat());
- assertEquals(sm5.mChildState2, pmi.getState());
- assertEquals(sm5.mChildState2, pmi.getOriginalState());
+ lr = sm5.getLogRec(1);
+ assertEquals(TEST_CMD_2, lr.getWhat());
+ assertEquals(sm5.mChildState2, lr.getState());
+ assertEquals(sm5.mChildState2, lr.getOriginalState());
- pmi = sm5.getProcessedMessageInfo(2);
- assertEquals(TEST_CMD_3, pmi.getWhat());
- assertEquals(sm5.mChildState5, pmi.getState());
- assertEquals(sm5.mChildState5, pmi.getOriginalState());
+ lr = sm5.getLogRec(2);
+ assertEquals(TEST_CMD_3, lr.getWhat());
+ assertEquals(sm5.mChildState5, lr.getState());
+ assertEquals(sm5.mChildState5, lr.getOriginalState());
- pmi = sm5.getProcessedMessageInfo(3);
- assertEquals(TEST_CMD_4, pmi.getWhat());
- assertEquals(sm5.mChildState3, pmi.getState());
- assertEquals(sm5.mChildState3, pmi.getOriginalState());
+ lr = sm5.getLogRec(3);
+ assertEquals(TEST_CMD_4, lr.getWhat());
+ assertEquals(sm5.mChildState3, lr.getState());
+ assertEquals(sm5.mChildState3, lr.getOriginalState());
- pmi = sm5.getProcessedMessageInfo(4);
- assertEquals(TEST_CMD_5, pmi.getWhat());
- assertEquals(sm5.mChildState4, pmi.getState());
- assertEquals(sm5.mChildState4, pmi.getOriginalState());
+ lr = sm5.getLogRec(4);
+ assertEquals(TEST_CMD_5, lr.getWhat());
+ assertEquals(sm5.mChildState4, lr.getState());
+ assertEquals(sm5.mChildState4, lr.getOriginalState());
- pmi = sm5.getProcessedMessageInfo(5);
- assertEquals(TEST_CMD_6, pmi.getWhat());
- assertEquals(sm5.mParentState2, pmi.getState());
- assertEquals(sm5.mParentState2, pmi.getOriginalState());
+ lr = sm5.getLogRec(5);
+ assertEquals(TEST_CMD_6, lr.getWhat());
+ assertEquals(sm5.mParentState2, lr.getState());
+ assertEquals(sm5.mParentState2, lr.getOriginalState());
if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X");
}
@@ -1161,7 +1295,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -1176,7 +1310,6 @@
@MediumTest
public void testStateMachine6() throws Exception {
- long sentTimeMsg2;
final int DELAY_TIME = 250;
final int DELAY_FUDGE = 20;
@@ -1186,7 +1319,6 @@
synchronized (sm6) {
// Send a message
- sentTimeMsg2 = SystemClock.elapsedRealtime();
sm6.sendMessageDelayed(TEST_CMD_2, DELAY_TIME);
try {
@@ -1268,7 +1400,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -1285,7 +1417,6 @@
@MediumTest
public void testStateMachine7() throws Exception {
- long sentTimeMsg2;
final int SM7_DELAY_FUDGE = 20;
StateMachine7 sm7 = new StateMachine7("sm7");
@@ -1294,7 +1425,6 @@
synchronized (sm7) {
// Send a message
- sentTimeMsg2 = SystemClock.elapsedRealtime();
sm7.sendMessage(TEST_CMD_1);
try {
@@ -1350,7 +1480,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
synchronized (mThisSm) {
mThisSm.notifyAll();
}
@@ -1383,7 +1513,7 @@
}
}
- assertTrue(sm.getProcessedMessagesCount() == 2);
+ assertEquals(sm.getLogRecCount(), 2);
assertEquals(2, sm.mUnhandledMessageCount);
if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X");
@@ -1420,7 +1550,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
// Update the shared counter, which is OK since all state
// machines are using the same thread.
sharedCounter += 1;
@@ -1470,12 +1600,12 @@
}
for (StateMachineSharedThread sm : sms) {
- assertTrue(sm.getProcessedMessagesCount() == 4);
- for (int i = 0; i < sm.getProcessedMessagesCount(); i++) {
- ProcessedMessageInfo pmi = sm.getProcessedMessageInfo(i);
- assertEquals(i+1, pmi.getWhat());
- assertEquals(sm.mS1, pmi.getState());
- assertEquals(sm.mS1, pmi.getOriginalState());
+ assertEquals(sm.getLogRecCount(), 4);
+ for (int i = 0; i < sm.getLogRecCount(); i++) {
+ LogRec lr = sm.getLogRec(i);
+ assertEquals(i+1, lr.getWhat());
+ assertEquals(sm.mS1, lr.getState());
+ assertEquals(sm.mS1, lr.getOriginalState());
}
}
@@ -1501,41 +1631,41 @@
}
}
- assertEquals(7, sm.getProcessedMessagesCount());
- ProcessedMessageInfo pmi = sm.getProcessedMessageInfo(0);
- assertEquals(Hsm1.CMD_1, pmi.getWhat());
- assertEquals(sm.mS1, pmi.getState());
- assertEquals(sm.mS1, pmi.getOriginalState());
+ assertEquals(7, sm.getLogRecCount());
+ LogRec lr = sm.getLogRec(0);
+ assertEquals(Hsm1.CMD_1, lr.getWhat());
+ assertEquals(sm.mS1, lr.getState());
+ assertEquals(sm.mS1, lr.getOriginalState());
- pmi = sm.getProcessedMessageInfo(1);
- assertEquals(Hsm1.CMD_2, pmi.getWhat());
- assertEquals(sm.mP1, pmi.getState());
- assertEquals(sm.mS1, pmi.getOriginalState());
+ lr = sm.getLogRec(1);
+ assertEquals(Hsm1.CMD_2, lr.getWhat());
+ assertEquals(sm.mP1, lr.getState());
+ assertEquals(sm.mS1, lr.getOriginalState());
- pmi = sm.getProcessedMessageInfo(2);
- assertEquals(Hsm1.CMD_2, pmi.getWhat());
- assertEquals(sm.mS2, pmi.getState());
- assertEquals(sm.mS2, pmi.getOriginalState());
+ lr = sm.getLogRec(2);
+ assertEquals(Hsm1.CMD_2, lr.getWhat());
+ assertEquals(sm.mS2, lr.getState());
+ assertEquals(sm.mS2, lr.getOriginalState());
- pmi = sm.getProcessedMessageInfo(3);
- assertEquals(Hsm1.CMD_3, pmi.getWhat());
- assertEquals(sm.mS2, pmi.getState());
- assertEquals(sm.mS2, pmi.getOriginalState());
+ lr = sm.getLogRec(3);
+ assertEquals(Hsm1.CMD_3, lr.getWhat());
+ assertEquals(sm.mS2, lr.getState());
+ assertEquals(sm.mS2, lr.getOriginalState());
- pmi = sm.getProcessedMessageInfo(4);
- assertEquals(Hsm1.CMD_3, pmi.getWhat());
- assertEquals(sm.mP2, pmi.getState());
- assertEquals(sm.mP2, pmi.getOriginalState());
+ lr = sm.getLogRec(4);
+ assertEquals(Hsm1.CMD_3, lr.getWhat());
+ assertEquals(sm.mP2, lr.getState());
+ assertEquals(sm.mP2, lr.getOriginalState());
- pmi = sm.getProcessedMessageInfo(5);
- assertEquals(Hsm1.CMD_4, pmi.getWhat());
- assertEquals(sm.mP2, pmi.getState());
- assertEquals(sm.mP2, pmi.getOriginalState());
+ lr = sm.getLogRec(5);
+ assertEquals(Hsm1.CMD_4, lr.getWhat());
+ assertEquals(sm.mP2, lr.getState());
+ assertEquals(sm.mP2, lr.getOriginalState());
- pmi = sm.getProcessedMessageInfo(6);
- assertEquals(Hsm1.CMD_5, pmi.getWhat());
- assertEquals(sm.mP2, pmi.getState());
- assertEquals(sm.mP2, pmi.getOriginalState());
+ lr = sm.getLogRec(6);
+ assertEquals(Hsm1.CMD_5, lr.getWhat());
+ assertEquals(sm.mP2, lr.getState());
+ assertEquals(sm.mP2, lr.getOriginalState());
if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
}
@@ -1684,7 +1814,7 @@
}
@Override
- protected void halting() {
+ protected void onHalting() {
Log.d(TAG, "halting");
synchronized (this) {
this.notifyAll();
diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
index cf32c9f..b1327f71 100644
--- a/core/tests/overlaytests/OverlayTestOverlay/Android.mk
+++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
@@ -9,6 +9,4 @@
LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay
-LOCAL_AAPT_FLAGS := -o
-
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg b/core/tests/overlaytests/OverlayTestOverlay/res/drawable-nodpi/default_wallpaper.jpg
similarity index 100%
rename from core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg
rename to core/tests/overlaytests/OverlayTestOverlay/res/drawable-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/tests/overlaytests/runtests.sh b/core/tests/overlaytests/runtests.sh
index 0ad9efb..0a721ad40 100755
--- a/core/tests/overlaytests/runtests.sh
+++ b/core/tests/overlaytests/runtests.sh
@@ -18,7 +18,6 @@
log=$(mktemp)
trap "atexit" EXIT
-failures=0
function compile_module()
{
@@ -38,6 +37,37 @@
$adb wait-for-device logcat | grep -m 1 -e 'PowerManagerService.*bootCompleted' >/dev/null
}
+function mkdir_if_needed()
+{
+ local path="$1"
+
+ if [[ "${path:0:1}" != "/" ]]; then
+ echo "mkdir_if_needed: error: path '$path' does not begin with /" | tee -a $log
+ exit 1
+ fi
+
+ local basename=$(basename "$path")
+ local dirname=$(dirname "$path")
+ local t=$($adb shell ls -l $dirname | tr -d '\r' | grep -e "${basename}$" | grep -oe '^.')
+
+ case "$t" in
+ d) # File exists, and is a directory ...
+ # do nothing
+ ;;
+ l) # ... (or symbolic link possibly to a directory).
+ # do nothing
+ ;;
+ "") # File does not exist.
+ mkdir_if_needed "$dirname"
+ $adb shell mkdir "$path"
+ ;;
+ *) # File exists, but is not a directory.
+ echo "mkdir_if_needed: file '$path' exists, but is not a directory" | tee -a $log
+ exit 1
+ ;;
+ esac
+}
+
function disable_overlay()
{
echo "Disabling overlay"
@@ -48,6 +78,8 @@
function enable_overlay()
{
echo "Enabling overlay"
+ mkdir_if_needed "/system/vendor"
+ mkdir_if_needed "/vendor/overlay/framework"
$adb shell ln -s /data/app/com.android.overlaytest.overlay.apk /vendor/overlay/framework/framework-res.apk
}
@@ -59,13 +91,21 @@
$adb shell am instrument -w -e class $class com.android.overlaytest/android.test.InstrumentationTestRunner | tee -a $log
}
+function remount()
+{
+ echo "Remounting file system writable"
+ $adb remount | tee -a $log
+}
+
function sync()
{
echo "Syncing to device"
- $adb remount | tee -a $log
$adb sync data | tee -a $log
}
+# some commands require write access, remount once and for all
+remount
+
# build and sync
compile_module "$PWD/OverlayTest/Android.mk"
compile_module "$PWD/OverlayTestOverlay/Android.mk"
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f3a1d9a..8cce191 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -5159,7 +5159,8 @@
const uint32_t pkg_id = pkg->package->id << 24;
for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
- ssize_t offset = -1;
+ ssize_t first = -1;
+ ssize_t last = -1;
const Type* typeConfigs = pkg->getType(typeIndex);
ssize_t mapIndex = map.add();
if (mapIndex < 0) {
@@ -5167,12 +5168,14 @@
}
Vector<uint32_t>& vector = map.editItemAt(mapIndex);
for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
- uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
+ uint32_t resID = pkg_id
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
resource_name resName;
if (!this->getResourceName(resID, &resName)) {
ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
+ // add dummy value, or trimming leading/trailing zeroes later will fail
+ vector.push(0);
continue;
}
@@ -5185,13 +5188,13 @@
overlayPackage.string(),
overlayPackage.size());
if (overlayResID != 0) {
- // overlay package has package ID == 0, use original package's ID instead
- overlayResID |= pkg_id;
+ overlayResID = pkg_id | (0x00ffffff & overlayResID);
+ last = Res_GETENTRY(resID);
+ if (first == -1) {
+ first = Res_GETENTRY(resID);
+ }
}
vector.push(overlayResID);
- if (overlayResID != 0 && offset == -1) {
- offset = Res_GETENTRY(resID);
- }
#if 0
if (overlayResID != 0) {
ALOGD("%s/%s 0x%08x -> 0x%08x\n",
@@ -5202,13 +5205,16 @@
#endif
}
- if (offset != -1) {
- // shave off leading and trailing entries which lack overlay values
- vector.removeItemsAt(0, offset);
- vector.insertAt((uint32_t)offset, 0, 1);
- while (vector.top() == 0) {
- vector.pop();
+ if (first != -1) {
+ // shave off trailing entries which lack overlay values
+ const size_t last_past_one = last + 1;
+ if (last_past_one < vector.size()) {
+ vector.removeItemsAt(last_past_one, vector.size() - last_past_one);
}
+ // shave off leading entries which lack overlay values
+ vector.removeItemsAt(0, first);
+ // store offset to first overlaid resource ID of this type
+ vector.insertAt((uint32_t)first, 0, 1);
// reserve space for number and offset of entries, and the actual entries
*outSize += (2 + vector.size()) * sizeof(uint32_t);
} else {
@@ -5246,6 +5252,10 @@
if (N == 0) {
continue;
}
+ if (N == 1) { // vector expected to hold (offset) + (N > 0 entries)
+ ALOGW("idmap: type %d supposedly has entries, but no entries found\n", i);
+ return UNKNOWN_ERROR;
+ }
*data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
for (size_t j = 0; j < N; ++j) {
const uint32_t& overlayResID = vector.itemAt(j);
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index c9bec18..d21ada4 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -172,6 +172,7 @@
static {
addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3);
+ addFileType("MPGA", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3);
addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", MtpConstants.FORMAT_MPEG);
addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", MtpConstants.FORMAT_WAV);
addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 2594167..423c802 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1557,6 +1557,12 @@
SystemProperties.get("ro.com.android.dataroaming",
"false")) ? 1 : 0);
+ // Mobile Data default, based on build
+ loadSetting(stmt, Settings.Secure.MOBILE_DATA,
+ "true".equalsIgnoreCase(
+ SystemProperties.get("ro.com.android.mobiledata",
+ "true")) ? 1 : 0);
+
loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
R.bool.def_install_non_market_apps);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 96f83c6..b0a7c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -294,6 +294,13 @@
updateWallpaperLocked();
}
+ if (mBackground == null) {
+ // If we somehow got to this point after we have last flushed
+ // the wallpaper, well we really need it to draw again. So
+ // seems like we need to reload it. Ouch.
+ updateWallpaperLocked();
+ }
+
SurfaceHolder sh = getSurfaceHolder();
final Rect frame = sh.getSurfaceFrame();
final int dw = frame.width();
@@ -315,7 +322,6 @@
mLastXTranslation = xPixels;
mLastYTranslation = yPixels;
-
if (mIsHwAccelerated) {
if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 47703c6..0aa3018 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1666,7 +1666,9 @@
*/
private void restorePanelState(SparseArray<Parcelable> icicles) {
PanelFeatureState st;
- for (int curFeatureId = icicles.size() - 1; curFeatureId >= 0; curFeatureId--) {
+ int curFeatureId;
+ for (int i = icicles.size() - 1; i >= 0; i--) {
+ curFeatureId = icicles.keyAt(i);
st = getPanelState(curFeatureId, false /* required */);
if (st == null) {
// The panel must not have been required, and is currently not around, skip it
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 7609613..ea19d6e 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1691,6 +1691,7 @@
// Note: we can only do the wipe via ExternalStorageFormatter if the volume is not emulated.
if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) {
Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
+ intent.putExtra(ExternalStorageFormatter.EXTRA_ALWAYS_RESET, true);
intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
mWakeLock.acquire(10000);
mContext.startService(intent);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 1482d22..04267a3 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1221,6 +1221,7 @@
for(MountServiceBinderListener bl : mListeners) {
if (bl.mListener == listener) {
mListeners.remove(mListeners.indexOf(bl));
+ listener.asBinder().unlinkToDeath(bl, 0);
return;
}
}
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index 6ad8bd0..87843d9 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -110,8 +110,8 @@
private final EnabledState mEnabledState = new EnabledState();
@Override
- protected String getMessageInfo(Message msg) {
- return cmdToString(msg.what);
+ protected String getWhatToString(int what) {
+ return cmdToString(what);
}
/**
@@ -144,7 +144,7 @@
} else {
setInitialState(mDisabledState);
}
- setProcessedMessagesSize(25);
+ setLogRecSize(25);
registerForNsdSetting();
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 24c59a5..888ec69 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1165,6 +1165,8 @@
? "SCREEN_BRIGHT_BIT " : "")
+ (((state & SCREEN_ON_BIT) != 0)
? "SCREEN_ON_BIT " : "")
+ + (((state & BUTTON_BRIGHT_BIT) != 0)
+ ? "BUTTON_BRIGHT_BIT " : "")
+ (((state & BATTERY_LOW_BIT) != 0)
? "BATTERY_LOW_BIT " : "");
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9dd4a91..f300d74 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,7 +83,7 @@
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
SystemClock.uptimeMillis());
- Looper.prepare();
+ Looper.prepareMainLooper();
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 95d3c41..60085f4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1820,7 +1820,7 @@
if (cr.binding != null && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq) {
- updateLruProcessInternalLocked(cr.binding.service.app, oomAdj,
+ updateLruProcessInternalLocked(cr.binding.service.app, false,
updateActivityTime, i+1);
}
}
@@ -1828,7 +1828,7 @@
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
- updateLruProcessInternalLocked(cpr.proc, oomAdj,
+ updateLruProcessInternalLocked(cpr.proc, false,
updateActivityTime, i+1);
}
}
@@ -2053,7 +2053,7 @@
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags,
- app.info.targetSdkVersion, null);
+ app.info.targetSdkVersion, null, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
synchronized (bs) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index d031686..120b650 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1241,7 +1241,7 @@
// Avoid any application that has a space in its path
// or that is handled by the system.
- if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID)
+ if (dataPath.indexOf(" ") >= 0 || ai.uid < Process.FIRST_APPLICATION_UID)
continue;
// we store on each line the following information for now:
diff --git a/services/java/com/android/server/pm/ShutdownThread.java b/services/java/com/android/server/pm/ShutdownThread.java
index 69406c8..3675d41 100644
--- a/services/java/com/android/server/pm/ShutdownThread.java
+++ b/services/java/com/android/server/pm/ShutdownThread.java
@@ -84,6 +84,8 @@
private PowerManager.WakeLock mCpuWakeLock;
private PowerManager.WakeLock mScreenWakeLock;
private Handler mHandler;
+
+ private static AlertDialog sConfirmDialog;
private ShutdownThread() {
}
@@ -124,7 +126,10 @@
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
- final AlertDialog dialog = new AlertDialog.Builder(context)
+ if (sConfirmDialog != null) {
+ sConfirmDialog.dismiss();
+ }
+ sConfirmDialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
@@ -136,10 +141,10 @@
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
- closer.dialog = dialog;
- dialog.setOnDismissListener(closer);
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- dialog.show();
+ closer.dialog = sConfirmDialog;
+ sConfirmDialog.setOnDismissListener(closer);
+ sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ sConfirmDialog.show();
} else {
beginShutdownSequence(context);
}
diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
index 84db830..6cfae6a 100644
--- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java
+++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
@@ -81,14 +81,26 @@
}
/**
- * @return cdma base station latitude, Integer.MAX_VALUE if unknown
+ * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf)
+ * It is represented in units of 0.25 seconds and ranges from -1296000
+ * to 1296000, both values inclusive (corresponding to a range of -90
+ * to +90 degrees). Integer.MAX_VALUE is considered invalid value.
+ *
+ * @return cdma base station latitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown
*/
public int getBaseStationLatitude() {
return this.mBaseStationLatitude;
}
/**
- * @return cdma base station longitude, Integer.MAX_VALUE if unknown
+ * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf)
+ * It is represented in units of 0.25 seconds and ranges from -2592000
+ * to 2592000, both values inclusive (corresponding to a range of -180
+ * to +180 degrees). Integer.MAX_VALUE is considered invalid value.
+ *
+ * @return cdma base station longitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown
*/
public int getBaseStationLongitude() {
return this.mBaseStationLongitude;
@@ -215,6 +227,22 @@
this.mNetworkId == -1);
}
+ /**
+ * Converts latitude or longitude from 0.25 seconds (as defined in the
+ * 3GPP2 C.S0005-A v6.0 standard) to decimal degrees
+ *
+ * @param quartSec latitude or longitude in 0.25 seconds units
+ * @return latitude or longitude in decimal degrees units
+ * @throws IllegalArgumentException if value is less than -2592000,
+ * greater than 2592000, or is not a number.
+ */
+ public static double convertQuartSecToDecDegrees(int quartSec) {
+ if(Double.isNaN(quartSec) || quartSec < -2592000 || quartSec > 2592000){
+ // Invalid value
+ throw new IllegalArgumentException("Invalid coordiante value:" + quartSec);
+ }
+ return ((double)quartSec) / (3600 * 4);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index 07f90cd..021602f 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -28,6 +28,10 @@
public static int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network
public static int PRESENTATION_PAYPHONE = 4; // show pay phone info
+ //Caller Name Display
+ protected String cnapName;
+ protected int cnapNamePresentation = PRESENTATION_ALLOWED;
+
private static String LOG_TAG = "TelephonyConnection";
public enum DisconnectCause {
@@ -84,11 +88,11 @@
public abstract String getAddress();
/**
- * Gets CDMA CNAP name associated with connection.
+ * Gets CNAP name associated with connection.
* @return cnap name or null if unavailable
*/
public String getCnapName() {
- return null;
+ return cnapName;
}
/**
@@ -100,12 +104,12 @@
}
/**
- * Gets CDMA CNAP presentation associated with connection.
+ * Gets CNAP presentation associated with connection.
* @return cnap name or null if unavailable
*/
public int getCnapNamePresentation() {
- return 0;
+ return cnapNamePresentation;
};
/**
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index cc4adfd..a6bd85e 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -17,7 +17,6 @@
package com.android.internal.telephony;
-import com.android.internal.telephony.DataCallState.SetupResult;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
@@ -39,6 +38,7 @@
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* {@hide}
@@ -65,8 +65,7 @@
protected static final boolean DBG = true;
protected static final boolean VDBG = false;
- protected static Object mCountLock = new Object();
- protected static int mCount;
+ protected static AtomicInteger mCount = new AtomicInteger(0);
protected AsyncChannel mAc;
protected List<ApnContext> mApnList = null;
@@ -260,12 +259,11 @@
protected abstract void log(String s);
-
//***** Constructor
protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
DataConnectionTracker dct) {
super(name);
- setProcessedMessagesSize(100);
+ setLogRecSize(100);
if (DBG) log("DataConnection constructor E");
this.phone = phone;
this.mDataConnectionTracker = dct;
@@ -287,6 +285,27 @@
}
/**
+ * Shut down this instance and its state machine.
+ */
+ private void shutDown() {
+ if (DBG) log("shutDown");
+
+ if (mAc != null) {
+ mAc.disconnected();
+ mAc = null;
+ }
+ mApnList = null;
+ mReconnectIntent = null;
+ mDataConnectionTracker = null;
+ mApn = null;
+ phone = null;
+ mLinkProperties = null;
+ mCapabilities = null;
+ lastFailCause = null;
+ userData = null;
+ }
+
+ /**
* TearDown the data connection.
*
* @param o will be returned in AsyncResult.userObj
@@ -619,9 +638,11 @@
@Override
public void exit() {
phone.mCM.unregisterForRilConnected(getHandler());
+ shutDown();
}
@Override
public boolean processMessage(Message msg) {
+ boolean retVal = HANDLED;
AsyncResult ar;
switch (msg.what) {
@@ -639,14 +660,9 @@
}
break;
}
- case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
- if (VDBG) log("CMD_CHANNEL_DISCONNECT");
- mAc.disconnect();
- break;
- }
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
- mAc = null;
+ quit();
break;
}
case DataConnectionAc.REQ_IS_INACTIVE: {
@@ -784,7 +800,7 @@
break;
}
- return HANDLED;
+ return retVal;
}
}
private DcDefaultState mDefaultState = new DcDefaultState();
@@ -1215,11 +1231,11 @@
* @return the string for msg.what as our info.
*/
@Override
- protected String getMessageInfo(Message msg) {
+ protected String getWhatToString(int what) {
String info = null;
- info = cmdToString(msg.what);
+ info = cmdToString(what);
if (info == null) {
- info = DataConnectionAc.cmdToString(msg.what);
+ info = DataConnectionAc.cmdToString(what);
}
return info;
}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 07d733e..41125dd 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -116,6 +116,12 @@
/** Don't send SMS (user did not confirm). */
static final int EVENT_STOP_SENDING = 7; // accessed from inner class
+ /** Confirmation required for third-party apps sending to an SMS short code. */
+ private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
+
+ /** Confirmation required for third-party apps sending to an SMS short code. */
+ private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
+
protected final Phone mPhone;
protected final Context mContext;
protected final ContentResolver mResolver;
@@ -288,6 +294,14 @@
handleReachSentLimit((SmsTracker)(msg.obj));
break;
+ case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(false, (SmsTracker)(msg.obj));
+ break;
+
+ case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(true, (SmsTracker)(msg.obj));
+ break;
+
case EVENT_SEND_CONFIRMED_SMS:
{
SmsTracker tracker = (SmsTracker) msg.obj;
@@ -906,18 +920,61 @@
SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appPackage,
PhoneNumberUtils.extractNetworkPortion(destAddr));
- // check for excessive outgoing SMS usage by this app
- if (!mUsageMonitor.check(appPackage, SINGLE_PART_SMS)) {
- sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
- return;
+ // checkDestination() returns true if the destination is not a premium short code or the
+ // sending app is approved to send to short codes. Otherwise, a message is sent to our
+ // handler with the SmsTracker to request user confirmation before sending.
+ if (checkDestination(tracker)) {
+ // check for excessive outgoing SMS usage by this app
+ if (!mUsageMonitor.check(appPackage, SINGLE_PART_SMS)) {
+ sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
+ return;
+ }
+
+ int ss = mPhone.getServiceState().getState();
+
+ if (ss != ServiceState.STATE_IN_SERVICE) {
+ handleNotInService(ss, tracker.mSentIntent);
+ } else {
+ sendSms(tracker);
+ }
}
+ }
- int ss = mPhone.getServiceState().getState();
-
- if (ss != ServiceState.STATE_IN_SERVICE) {
- handleNotInService(ss, tracker.mSentIntent);
+ /**
+ * Check if destination is a potential premium short code and sender is not pre-approved to
+ * send to short codes.
+ *
+ * @param tracker the tracker for the SMS to send
+ * @return true if the destination is approved; false if user confirmation event was sent
+ */
+ boolean checkDestination(SmsTracker tracker) {
+ if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true; // app is pre-approved to send to short codes
} else {
- sendSms(tracker);
+ String countryIso = mTelephonyManager.getSimCountryIso();
+ if (countryIso == null || countryIso.length() != 2) {
+ Log.e(TAG, "Can't get SIM country code: trying network country code");
+ countryIso = mTelephonyManager.getNetworkCountryIso();
+ }
+
+ switch (mUsageMonitor.checkDestination(tracker.mDestAddress, countryIso)) {
+ case SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE:
+ sendMessage(obtainMessage(EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE,
+ tracker));
+ return false; // wait for user confirmation before sending
+
+ case SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE:
+ sendMessage(obtainMessage(EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE,
+ tracker));
+ return false; // wait for user confirmation before sending
+
+ case SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE:
+ case SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE:
+ case SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE:
+ default:
+ return true; // destination is not a premium short code
+ }
}
}
@@ -986,6 +1043,47 @@
}
/**
+ * Post an alert for user confirmation when sending to a potential short code.
+ * @param isPremium true if the destination is known to be a premium short code
+ * @param tracker the SmsTracker for the current message.
+ */
+ protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
+ if (denyIfQueueLimitReached(tracker)) {
+ return; // queue limit reached; error was returned to caller
+ }
+
+ int messageId;
+ int titleId;
+ if (isPremium) {
+ messageId = R.string.sms_premium_short_code_confirm_message;
+ titleId = R.string.sms_premium_short_code_confirm_title;
+ } else {
+ messageId = R.string.sms_short_code_confirm_message;
+ titleId = R.string.sms_short_code_confirm_title;
+ }
+
+ CharSequence appLabel = getAppLabel(tracker.mAppPackage);
+ Resources r = Resources.getSystem();
+ Spanned messageText = Html.fromHtml(r.getString(messageId, appLabel, tracker.mDestAddress));
+
+ ConfirmDialogListener listener = new ConfirmDialogListener(tracker);
+
+ AlertDialog d = new AlertDialog.Builder(mContext)
+ .setTitle(titleId)
+ .setIcon(R.drawable.stat_sys_warning)
+ .setMessage(messageText)
+ .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
+ .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
+// TODO: add third button for "Report malicious app" feature
+// .setNeutralButton(r.getString(R.string.sms_short_code_confirm_report), listener)
+ .setOnCancelListener(listener)
+ .create();
+
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.show();
+ }
+
+ /**
* Send the message along to the radio.
*
* @param tracker holds the SMS message to send
diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
index 4a4485d..1804d97 100644
--- a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -60,17 +60,177 @@
/** Default number of SMS sent in checking period without user permission. */
private static final int DEFAULT_SMS_MAX_COUNT = 30;
+ /** Return value from {@link #checkDestination} for regular phone numbers. */
+ static final int CATEGORY_NOT_SHORT_CODE = 0;
+
+ /** Return value from {@link #checkDestination} for free (no cost) short codes. */
+ static final int CATEGORY_FREE_SHORT_CODE = 1;
+
+ /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */
+ static final int CATEGORY_STANDARD_SHORT_CODE = 2;
+
+ /** Return value from {@link #checkDestination} for possible premium short codes. */
+ static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+
+ /** Return value from {@link #checkDestination} for premium short codes. */
+ static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
+
private final int mCheckPeriod;
private final int mMaxAllowed;
private final HashMap<String, ArrayList<Long>> mSmsStamp =
new HashMap<String, ArrayList<Long>>();
+ /** Context for retrieving regexes from XML resource. */
+ private final Context mContext;
+
+ /** Country code for the cached short code pattern matcher. */
+ private String mCurrentCountry;
+
+ /** Cached short code pattern matcher for {@link #mCurrentCountry}. */
+ private ShortCodePatternMatcher mCurrentPatternMatcher;
+
+ /** Cached short code regex patterns from secure settings for {@link #mCurrentCountry}. */
+ private String mSettingsShortCodePatterns;
+
+ /** Handler for responding to content observer updates. */
+ private final SettingsObserverHandler mSettingsObserverHandler;
+
+ /** XML tag for root element. */
+ private static final String TAG_SHORTCODES = "shortcodes";
+
+ /** XML tag for short code patterns for a specific country. */
+ private static final String TAG_SHORTCODE = "shortcode";
+
+ /** XML attribute for the country code. */
+ private static final String ATTR_COUNTRY = "country";
+
+ /** XML attribute for the short code regex pattern. */
+ private static final String ATTR_PATTERN = "pattern";
+
+ /** XML attribute for the premium short code regex pattern. */
+ private static final String ATTR_PREMIUM = "premium";
+
+ /** XML attribute for the free short code regex pattern. */
+ private static final String ATTR_FREE = "free";
+
+ /** XML attribute for the standard rate short code regex pattern. */
+ private static final String ATTR_STANDARD = "standard";
+
+ /**
+ * SMS short code regex pattern matcher for a specific country.
+ */
+ private static final class ShortCodePatternMatcher {
+ private final Pattern mShortCodePattern;
+ private final Pattern mPremiumShortCodePattern;
+ private final Pattern mFreeShortCodePattern;
+ private final Pattern mStandardShortCodePattern;
+
+ ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex,
+ String freeShortCodeRegex, String standardShortCodeRegex) {
+ mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null);
+ mPremiumShortCodePattern = (premiumShortCodeRegex != null ?
+ Pattern.compile(premiumShortCodeRegex) : null);
+ mFreeShortCodePattern = (freeShortCodeRegex != null ?
+ Pattern.compile(freeShortCodeRegex) : null);
+ mStandardShortCodePattern = (standardShortCodeRegex != null ?
+ Pattern.compile(standardShortCodeRegex) : null);
+ }
+
+ int getNumberCategory(String phoneNumber) {
+ if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber)
+ .matches()) {
+ return CATEGORY_FREE_SHORT_CODE;
+ }
+ if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber)
+ .matches()) {
+ return CATEGORY_STANDARD_SHORT_CODE;
+ }
+ if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber)
+ .matches()) {
+ return CATEGORY_PREMIUM_SHORT_CODE;
+ }
+ if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) {
+ return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+ }
+ return CATEGORY_NOT_SHORT_CODE;
+ }
+ }
+
+ /**
+ * Observe the secure setting for updated regex patterns.
+ */
+ private static class SettingsObserver extends ContentObserver {
+ private final int mWhat;
+ private final Handler mHandler;
+
+ SettingsObserver(Handler handler, int what) {
+ super(handler);
+ mHandler = handler;
+ mWhat = what;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mWhat).sendToTarget();
+ }
+ }
+
+ /**
+ * Handler to update regex patterns when secure setting for the current country is updated.
+ */
+ private class SettingsObserverHandler extends Handler {
+ /** Current content observer, or null. */
+ SettingsObserver mSettingsObserver;
+
+ /** Current country code to watch for settings updates. */
+ private String mCountryIso;
+
+ /** Request to start observing a secure setting. */
+ static final int OBSERVE_SETTING = 1;
+
+ /** Handler event for updated secure settings. */
+ static final int SECURE_SETTINGS_CHANGED = 2;
+
+ /** Send a message to this handler requesting to observe the setting for a new country. */
+ void observeSettingForCountry(String countryIso) {
+ obtainMessage(OBSERVE_SETTING, countryIso).sendToTarget();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OBSERVE_SETTING:
+ if (msg.obj != null && msg.obj instanceof String) {
+ mCountryIso = (String) msg.obj;
+ String settingName = getSettingNameForCountry(mCountryIso);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ if (mSettingsObserver != null) {
+ if (VDBG) log("Unregistering old content observer");
+ resolver.unregisterContentObserver(mSettingsObserver);
+ }
+
+ mSettingsObserver = new SettingsObserver(this, SECURE_SETTINGS_CHANGED);
+ resolver.registerContentObserver(
+ Settings.Secure.getUriFor(settingName), false, mSettingsObserver);
+ if (VDBG) log("Registered content observer for " + settingName);
+ }
+ break;
+
+ case SECURE_SETTINGS_CHANGED:
+ loadPatternsFromSettings(mCountryIso);
+ break;
+ }
+ }
+ }
+
/**
* Create SMS usage monitor.
* @param context the context to use to load resources and get TelephonyManager service
*/
public SmsUsageMonitor(Context context) {
+ mContext = context;
ContentResolver resolver = context.getContentResolver();
mMaxAllowed = Settings.Secure.getInt(resolver,
@@ -80,6 +240,83 @@
mCheckPeriod = Settings.Secure.getInt(resolver,
Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
DEFAULT_SMS_CHECK_PERIOD);
+
+ mSettingsObserverHandler = new SettingsObserverHandler();
+ }
+
+ /**
+ * Return a pattern matcher object for the specified country.
+ * @param country the country to search for
+ * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+ */
+ private ShortCodePatternMatcher getPatternMatcher(String country) {
+ int id = com.android.internal.R.xml.sms_short_codes;
+ XmlResourceParser parser = mContext.getResources().getXml(id);
+
+ try {
+ return getPatternMatcher(country, parser);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "XML parser exception reading short code pattern resource", e);
+ } catch (IOException e) {
+ Log.e(TAG, "I/O exception reading short code pattern resource", e);
+ } finally {
+ parser.close();
+ }
+ return null; // country not found
+ }
+
+ /**
+ * Return a pattern matcher object for the specified country from a secure settings string.
+ * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+ */
+ private static ShortCodePatternMatcher getPatternMatcher(String country, String settingsPattern) {
+ // embed pattern tag into an XML document.
+ String document = "<shortcodes>" + settingsPattern + "</shortcodes>";
+ if (VDBG) log("loading updated patterns from: " + document);
+
+ try {
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ XmlPullParser parser = factory.newPullParser();
+ parser.setInput(new StringReader(document));
+ return getPatternMatcher(country, parser);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "XML parser exception reading short code pattern from settings", e);
+ } catch (IOException e) {
+ Log.e(TAG, "I/O exception reading short code pattern from settings", e);
+ }
+ return null; // country not found
+ }
+
+ /**
+ * Return a pattern matcher object for the specified country and pattern XML parser.
+ * @param country the country to search for
+ * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+ */
+ private static ShortCodePatternMatcher getPatternMatcher(String country, XmlPullParser parser)
+ throws XmlPullParserException, IOException
+ {
+ XmlUtils.beginDocument(parser, TAG_SHORTCODES);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ String element = parser.getName();
+ if (element == null) break;
+
+ if (element.equals(TAG_SHORTCODE)) {
+ String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
+ if (country.equals(currentCountry)) {
+ String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
+ String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
+ String free = parser.getAttributeValue(null, ATTR_FREE);
+ String standard = parser.getAttributeValue(null, ATTR_STANDARD);
+ return new ShortCodePatternMatcher(pattern, premium, free, standard);
+ }
+ } else {
+ Log.e(TAG, "Error: skipping unknown XML tag " + element);
+ }
+ }
+ return null; // country not found
}
/** Clear the SMS application list for disposal. */
@@ -112,6 +349,90 @@
}
/**
+ * Check if the destination is a possible premium short code.
+ * NOTE: the caller is expected to strip non-digits from the destination number with
+ * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
+ * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number
+ * for testing and in the user confirmation dialog if the user needs to confirm the number.
+ * This makes it difficult for malware to fool the user or the short code pattern matcher
+ * by using non-ASCII characters to make the number appear to be different from the real
+ * destination phone number.
+ *
+ * @param destAddress the destination address to test for possible short code
+ * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE},
+ * {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
+ */
+ public int checkDestination(String destAddress, String countryIso) {
+ synchronized (mSettingsObserverHandler) {
+ // always allow emergency numbers
+ if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
+ return CATEGORY_NOT_SHORT_CODE;
+ }
+
+ ShortCodePatternMatcher patternMatcher = null;
+
+ if (countryIso != null) {
+ // query secure settings and initialize content observer for updated regex patterns
+ if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry)) {
+ loadPatternsFromSettings(countryIso);
+ mSettingsObserverHandler.observeSettingForCountry(countryIso);
+ }
+
+ if (countryIso.equals(mCurrentCountry)) {
+ patternMatcher = mCurrentPatternMatcher;
+ } else {
+ patternMatcher = getPatternMatcher(countryIso);
+ mCurrentCountry = countryIso;
+ mCurrentPatternMatcher = patternMatcher; // may be null if not found
+ }
+ }
+
+ if (patternMatcher != null) {
+ return patternMatcher.getNumberCategory(destAddress);
+ } else {
+ // Generic rule: numbers of 5 digits or less are considered potential short codes
+ Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
+ if (destAddress.length() <= 5) {
+ return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+ } else {
+ return CATEGORY_NOT_SHORT_CODE;
+ }
+ }
+ }
+ }
+
+ private static String getSettingNameForCountry(String countryIso) {
+ return Settings.Secure.SMS_SHORT_CODES_PREFIX + countryIso;
+ }
+
+ /**
+ * Load regex patterns from secure settings if present.
+ * @param countryIso the country to search for
+ */
+ void loadPatternsFromSettings(String countryIso) {
+ synchronized (mSettingsObserverHandler) {
+ if (VDBG) log("loadPatternsFromSettings(" + countryIso + ") called");
+ String settingsPatterns = Settings.Secure.getString(
+ mContext.getContentResolver(), getSettingNameForCountry(countryIso));
+ if (settingsPatterns != null && !settingsPatterns.equals(
+ mSettingsShortCodePatterns)) {
+ // settings pattern string has changed: update the pattern matcher
+ mSettingsShortCodePatterns = settingsPatterns;
+ ShortCodePatternMatcher matcher = getPatternMatcher(countryIso, settingsPatterns);
+ if (matcher != null) {
+ mCurrentCountry = countryIso;
+ mCurrentPatternMatcher = matcher;
+ }
+ } else if (settingsPatterns == null && mSettingsShortCodePatterns != null) {
+ // pattern string was removed: caller will load default patterns from XML resource
+ mCurrentCountry = null;
+ mCurrentPatternMatcher = null;
+ mSettingsShortCodePatterns = null;
+ }
+ }
+ }
+
+ /**
* Remove keys containing only old timestamps. This can happen if an SMS app is used
* to send messages and then uninstalled.
*/
diff --git a/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java b/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java
index bd94de2..a122e68 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -177,4 +177,15 @@
public static boolean supportsAdn(int phoneType) {
return phoneType == Phone.PHONE_TYPE_GSM;
}
+
+ /**
+ * Returns true if the device can distinguish the phone's dialing state
+ * (Call.State.DIALING/ALERTING) and connected state (Call.State.ACTIVE).
+ *
+ * Currently this returns true for GSM phones as we cannot know when a CDMA
+ * phone has transitioned from dialing/active to connected.
+ */
+ public static boolean canDistinguishDialingAndConnected(int phoneType) {
+ return phoneType == Phone.PHONE_TYPE_GSM;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandDetails.java b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
index 8579535..3e7f722 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
@@ -48,13 +48,14 @@
}
public CommandDetails(Parcel in) {
- compRequired = true;
+ compRequired = in.readInt() != 0;
commandNumber = in.readInt();
typeOfCommand = in.readInt();
commandQualifier = in.readInt();
}
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(compRequired ? 1 : 0);
dest.writeInt(commandNumber);
dest.writeInt(typeOfCommand);
dest.writeInt(commandQualifier);
@@ -111,4 +112,4 @@
ComprehensionTlvTag getTag() {
return ComprehensionTlvTag.ITEM_ICON_ID_LIST;
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 98ad3b1..6985979 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -51,7 +51,6 @@
String postDialString; // outgoing calls only
boolean isIncoming;
boolean disconnected;
- String cnapName;
int index; // index in CdmaCallTracker.connections[], -1 if unassigned
/*
@@ -77,7 +76,6 @@
DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED;
PostDialState postDialState = PostDialState.NOT_STARTED;
int numberPresentation = Connection.PRESENTATION_ALLOWED;
- int cnapNamePresentation = Connection.PRESENTATION_ALLOWED;
Handler h;
@@ -230,14 +228,6 @@
return address;
}
- public String getCnapName() {
- return cnapName;
- }
-
- public int getCnapNamePresentation() {
- return cnapNamePresentation;
- }
-
public CdmaCall getCall() {
return parent;
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index 4ef05ea..141736c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -51,11 +51,8 @@
*/
static CdmaDataConnection makeDataConnection(CDMAPhone phone, int id, RetryManager rm,
DataConnectionTracker dct) {
- synchronized (mCountLock) {
- mCount += 1;
- }
- CdmaDataConnection cdmaDc = new CdmaDataConnection(phone, "CdmaDC-" + mCount,
- id, rm, dct);
+ CdmaDataConnection cdmaDc = new CdmaDataConnection(phone,
+ "CdmaDC-" + mCount.incrementAndGet(), id, rm, dct);
cdmaDc.start();
if (DBG) cdmaDc.log("Made " + cdmaDc.getName());
return cdmaDc;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 6e9cd51..484bab0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -1232,7 +1232,7 @@
// If the radio shuts off or resets while one of these
// is pending, we need to clean up.
- for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
+ for (int i = mPendingMMIs.size() - 1; i >= 0; i--) {
if (mPendingMMIs.get(i).isPendingUSSD()) {
mPendingMMIs.get(i).onUssdFinishedError();
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
index f7c6025..9e32e8d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
@@ -26,6 +26,7 @@
import android.util.Log;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.text.TextUtils;
import com.android.internal.telephony.*;
@@ -125,6 +126,8 @@
isIncoming = dc.isMT;
createTime = System.currentTimeMillis();
+ cnapName = dc.name;
+ cnapNamePresentation = dc.namePresentation;
numberPresentation = dc.numberPresentation;
uusInfo = dc.uusInfo;
@@ -151,6 +154,9 @@
index = -1;
isIncoming = false;
+ cnapName = null;
+ cnapNamePresentation = Connection.PRESENTATION_ALLOWED;
+ numberPresentation = Connection.PRESENTATION_ALLOWED;
createTime = System.currentTimeMillis();
this.parent = parent;
@@ -437,6 +443,21 @@
changed = true;
}
+ // A null cnapName should be the same as ""
+ if (TextUtils.isEmpty(dc.name)) {
+ if (!TextUtils.isEmpty(cnapName)) {
+ changed = true;
+ cnapName = "";
+ }
+ } else if (!dc.name.equals(cnapName)) {
+ changed = true;
+ cnapName = dc.name;
+ }
+
+ if (Phone.DEBUG_PHONE) log("--dssds----"+cnapName);
+ cnapNamePresentation = dc.namePresentation;
+ numberPresentation = dc.numberPresentation;
+
if (newParent != parent) {
if (parent != null) {
parent.detach(this);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 9801721..3e6d9a5 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -56,10 +56,8 @@
*/
static GsmDataConnection makeDataConnection(PhoneBase phone, int id, RetryManager rm,
DataConnectionTracker dct) {
- synchronized (mCountLock) {
- mCount += 1;
- }
- GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDC-" + mCount, id, rm, dct);
+ GsmDataConnection gsmDc = new GsmDataConnection(phone,
+ "GsmDC-" + mCount.incrementAndGet(), id, rm, dct);
gsmDc.start();
if (DBG) gsmDc.log("Made " + gsmDc.getName());
return gsmDc;
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
new file mode 100644
index 0000000..3bb7c06
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
@@ -0,0 +1,466 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE;
+
+/**
+ * Test cases for SMS short code pattern matching in SmsUsageMonitor.
+ */
+public class SmsUsageMonitorShortCodeTest extends AndroidTestCase {
+
+ private static final class ShortCodeTest {
+ final String countryIso;
+ final String address;
+ final int category;
+
+ ShortCodeTest(String countryIso, String destAddress, int category) {
+ this.countryIso = countryIso;
+ this.address = destAddress;
+ this.category = category;
+ }
+ }
+
+ /**
+ * List of short code test cases.
+ */
+ private static final ShortCodeTest[] sShortCodeTests = new ShortCodeTest[] {
+ new ShortCodeTest("al", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("al", "4321", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("al", "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "15191", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "55500", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "55600", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("al", "654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("am", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("am", "101", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("am", "102", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("am", "103", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("am", "222", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1111", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "9999", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1121", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1141", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "1161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("am", "3024", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("at", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("at", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("at", "0901234", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("at", "0900666266", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("au", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("au", "180000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("au", "190000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("au", "1900000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("au", "19000000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("au", "19998882", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("az", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("az", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "87744", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "3301", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "3302", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "9012", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "9014", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "9394", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "87744", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "93101", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("az", "123456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("be", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("be", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("be", "567890", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("be", "8000", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("be", "6566", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("be", "7777", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("bg", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("bg", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("bg", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1816", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1915", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1916", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "1935", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("bg", "18423", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("by", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("by", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "3336", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "5013", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "5014", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("by", "7781", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("ca", "911", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "+18005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "8005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "20000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ca", "200000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ca", "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ca", "60999", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ca", "88188", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("ch", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ch", "123", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ch", "234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "3456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "98765", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "543", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "83111", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ch", "234567", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ch", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("cn", "120", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cn", "1062503000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cn", "1065123456", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("cn", "1066335588", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("cy", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cy", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("cy", "4321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "654321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "7510", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cy", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("cz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("cz", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("cz", "9090150", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cz", "90901599", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("cz", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("de", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("de", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("de", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "8888", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "11111", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "11886", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "22022", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "23300", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "3434", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "34567", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "41414", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "55655", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "66766", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "66777", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "77677", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "80888", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "1232286", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("de", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("dk", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("dk", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("dk", "1259", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("dk", "16123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("dk", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ee", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ee", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ee", "123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "1259", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "15330", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "17999", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "17010", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "17013", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "9034567", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ee", "34567890", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("es", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("es", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("es", "25165", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("es", "27333", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("es", "995399", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("es", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("fi", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("fi", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("fi", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "123456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "17159", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "17163", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "0600123", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "070012345", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fi", "987654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("fr", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("fr", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("fr", "34567", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("fr", "45678", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fr", "81185", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("fr", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("gb", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("gb", "999", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("gb", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("gb", "4567", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "45678", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "56789", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "79067", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "80079", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "654321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gb", "7654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ge", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ge", "8765", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "2345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8012", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8013", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8014", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ge", "8889", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("gr", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("gr", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("gr", "54321", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gr", "19567", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gr", "19678", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("gr", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("hu", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("hu", "012", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "0123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "1784", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "2345", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "01234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "012345678", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "0123456789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "1234567890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "0691227910", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("hu", "2345678901", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("hu", "01234567890", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ie", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ie", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ie", "50123", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ie", "51234", CATEGORY_STANDARD_SHORT_CODE),
+ new ShortCodeTest("ie", "52345", CATEGORY_STANDARD_SHORT_CODE),
+ new ShortCodeTest("ie", "57890", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ie", "67890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ie", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("il", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("il", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("il", "4422", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("il", "4545", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("il", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("it", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("it", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("it", "4567", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("it", "48000", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "45678", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "56789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "456789", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("kg", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("kg", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "4152", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "4157", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "4449", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kg", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("kz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("kz", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kz", "9194", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kz", "7790", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("kz", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("lt", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lt", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("lt", "123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1381", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1394", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "1645", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "12345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lt", "123456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("lu", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lu", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("lu", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lu", "12345", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lu", "64747", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lu", "678901", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("lv", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("lv", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("lv", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "1819", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "1863", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "1874", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("lv", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("mx", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("mx", "2345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("mx", "7766", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("mx", "23456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("mx", "53035", CATEGORY_PREMIUM_SHORT_CODE),
+
+ new ShortCodeTest("my", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("my", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("my", "23456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("my", "32298", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("my", "33776", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("my", "345678", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("nl", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("nl", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("nl", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nl", "4466", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nl", "5040", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nl", "23456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("no", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("no", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "2201", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "2226", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "2227", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "23456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("no", "234567", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("nz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("nz", "123", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "2345", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "3903", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "8995", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("nz", "23456", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("pl", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("pl", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("pl", "7890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "34567", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "7910", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "74240", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "79866", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "92525", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pl", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("pt", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("pt", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("pt", "61000", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "62345", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "68304", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "69876", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("pt", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ro", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ro", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("ro", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1263", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1288", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1314", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "1380", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "7890", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ro", "12345", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ru", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ru", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "1161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "2097", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "3933", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "7781", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ru", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("se", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("se", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("se", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("se", "72345", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("se", "72999", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("se", "123456", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("se", "87654321", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("sg", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("sg", "1234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("sg", "70000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sg", "79999", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sg", "73800", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sg", "74688", CATEGORY_STANDARD_SHORT_CODE),
+ new ShortCodeTest("sg", "987654", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("si", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("si", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("si", "1234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("si", "3838", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("si", "72999", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("sk", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("sk", "116117", CATEGORY_FREE_SHORT_CODE),
+ new ShortCodeTest("sk", "1234", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sk", "6674", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sk", "7604", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("sk", "72999", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("tj", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("tj", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "1161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "1171", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "4161", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "4449", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("tj", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("ua", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("ua", "5432", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "4448", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "7094", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "7540", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("ua", "98765", CATEGORY_NOT_SHORT_CODE),
+
+ new ShortCodeTest("us", "911", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "+18005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "8005551234", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "20000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "200000", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("us", "20433", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "21472", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "23333", CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("us", "99807", CATEGORY_PREMIUM_SHORT_CODE),
+
+ // generic rules for other countries: 5 digits or less considered potential short code
+ new ShortCodeTest("zz", "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest("zz", "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("zz", "4321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("zz", "321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("zz", "112", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest(null, "2000000", CATEGORY_NOT_SHORT_CODE),
+ new ShortCodeTest(null, "54321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest(null, "4321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest(null, "321", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest(null, "112", CATEGORY_NOT_SHORT_CODE),
+ };
+
+ @SmallTest
+ public void testSmsUsageMonitor() {
+ SmsUsageMonitor monitor = new SmsUsageMonitor(getContext());
+ for (ShortCodeTest test : sShortCodeTests) {
+ assertEquals("country: " + test.countryIso + " number: " + test.address,
+ test.category, monitor.checkDestination(test.address, test.countryIso));
+ }
+ }
+}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index f2fa3f5..46b8a27 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -58,7 +58,7 @@
// The default to use if no other ignore pattern is defined.
const char * const gDefaultIgnoreAssets =
- "!.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~";
+ "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
// The ignore pattern that can be passed via --ignore-assets in Main.cpp
const char * gUserIgnoreAssets = NULL;
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index d0a81dc..482f43e 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -29,6 +29,10 @@
LOCAL_CFLAGS += -Wno-format-y2k
+ifeq (darwin,$(HOST_OS))
+LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
+endif
+
LOCAL_C_INCLUDES += external/expat/lib
LOCAL_C_INCLUDES += external/libpng
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index daeadc0..a5aa0b5 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -51,9 +51,8 @@
mUpdate(false), mExtending(false),
mRequireLocalization(false), mPseudolocalize(false),
mWantUTF16(false), mValues(false),
- mCompressionMethod(0), mOutputAPKFile(NULL),
+ mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
- mIsOverlayPackage(false),
mAutoAddOverlay(false), mGenDependencies(false),
mAssetSourceDir(NULL),
mCrunchedOutputDir(NULL), mProguardFile(NULL),
@@ -107,8 +106,6 @@
void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; }
const char* getInstrumentationPackageNameOverride() const { return mInstrumentationPackageNameOverride; }
void setInstrumentationPackageNameOverride(const char * val) { mInstrumentationPackageNameOverride = val; }
- bool getIsOverlayPackage() const { return mIsOverlayPackage; }
- void setIsOverlayPackage(bool val) { mIsOverlayPackage = val; }
bool getAutoAddOverlay() { return mAutoAddOverlay; }
void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
bool getGenDependencies() { return mGenDependencies; }
@@ -250,7 +247,6 @@
const char* mOutputAPKFile;
const char* mManifestPackageNameOverride;
const char* mInstrumentationPackageNameOverride;
- bool mIsOverlayPackage;
bool mAutoAddOverlay;
bool mGenDependencies;
const char* mAssetSourceDir;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 9f05c6a..9570c66 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -69,7 +69,6 @@
" [-F apk-file] [-J R-file-dir] \\\n"
" [--product product1,product2,...] \\\n"
" [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
- " [-o] \\\n"
" [raw-files-dir [raw-files-dir] ...]\n"
"\n"
" Package the android resources. It will read assets and resources that are\n"
@@ -110,7 +109,6 @@
" -j specify a jar or zip file containing classes to include\n"
" -k junk path of file(s) added\n"
" -m make package directories under location specified by -J\n"
- " -o create overlay package (ie only resources; expects <overlay-package> in manifest)\n"
#if 0
" -p pseudolocalize the default configuration\n"
#endif
@@ -296,9 +294,6 @@
case 'm':
bundle.setMakePackageDirs(true);
break;
- case 'o':
- bundle.setIsOverlayPackage(true);
- break;
#if 0
case 'p':
bundle.setPseudolocalize(true);
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0195727..d98fe65 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2749,6 +2749,12 @@
const bool filterable = (typeName != mipmap16);
const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
+
+ // Until a non-NO_ENTRY value has been written for a resource,
+ // that resource is invalid; validResources[i] represents
+ // the item at t->getOrderedConfigs().itemAt(i).
+ Vector<bool> validResources;
+ validResources.insertAt(false, 0, N);
// First write the typeSpec chunk, containing information about
// each resource entry in this type.
@@ -2885,6 +2891,7 @@
if (amt < 0) {
return amt;
}
+ validResources.editItemAt(ei) = true;
} else {
index[ei] = htodl(ResTable_type::NO_ENTRY);
}
@@ -2895,6 +2902,14 @@
(((uint8_t*)data->editData()) + typeStart);
tHeader->header.size = htodl(data->getSize()-typeStart);
}
+
+ for (size_t i = 0; i < N; ++i) {
+ if (!validResources[i]) {
+ sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
+ fprintf(stderr, "warning: no entries written for %s/%s\n",
+ String8(typeName).string(), String8(c->getName()).string());
+ }
+ }
}
// Fill in the rest of the package information.
@@ -3723,9 +3738,7 @@
{
sp<Package> p = mPackages.valueFor(package);
if (p == NULL) {
- if (mBundle->getIsOverlayPackage()) {
- p = new Package(package, 0x00);
- } else if (mIsAppPackage) {
+ if (mIsAppPackage) {
if (mHaveAppPackage) {
fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
"Use -x to create extended resources.\n");
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 9fb000e..a5db7b1 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -3,7 +3,7 @@
<classpathentry excluding="org/kxml2/io/" kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
- <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/ninepatch/ninepatch-prebuilt.jar"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/tools-common/tools-common-prebuilt.jar"/>
diff --git a/tools/layoutlib/bridge/tests/.classpath b/tools/layoutlib/bridge/tests/.classpath
index 027bc67..2b32e09 100644
--- a/tools/layoutlib/bridge/tests/.classpath
+++ b/tools/layoutlib/bridge/tests/.classpath
@@ -4,7 +4,7 @@
<classpathentry kind="src" path="res"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/layoutlib_bridge"/>
- <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="output" path="bin"/>
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
new file mode 100755
index 0000000..c988c70
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
@@ -0,0 +1,787 @@
+/*
+ * 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 com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Analyzes the input JAR using the ASM java bytecode manipulation library
+ * to list the classes and their dependencies. A "dependency" is a class
+ * used by another class.
+ */
+public class DependencyFinder {
+
+ // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
+
+ /** Output logger. */
+ private final Log mLog;
+
+ /**
+ * Creates a new analyzer.
+ *
+ * @param log The log output.
+ */
+ public DependencyFinder(Log log) {
+ mLog = log;
+ }
+
+ /**
+ * Starts the analysis using parameters from the constructor.
+ *
+ * @param osJarPath The input source JARs to parse.
+ * @return A pair: [0]: map { class FQCN => set of FQCN class dependencies }.
+ * [1]: map { missing class FQCN => set of FQCN class that uses it. }
+ */
+ public List<Map<String, Set<String>>> findDeps(List<String> osJarPath) throws IOException {
+
+ Map<String, ClassReader> zipClasses = parseZip(osJarPath);
+ mLog.info("Found %d classes in input JAR%s.",
+ zipClasses.size(),
+ osJarPath.size() > 1 ? "s" : "");
+
+ Map<String, Set<String>> deps = findClassesDeps(zipClasses);
+
+ Map<String, Set<String>> missing = findMissingClasses(deps, zipClasses.keySet());
+
+ List<Map<String, Set<String>>> result = new ArrayList<Map<String,Set<String>>>(2);
+ result.add(deps);
+ result.add(missing);
+ return result;
+ }
+
+ /**
+ * Prints dependencies to the current logger, found stuff and missing stuff.
+ */
+ public void printAllDeps(List<Map<String, Set<String>>> result) {
+ assert result.size() == 2;
+ Map<String, Set<String>> deps = result.get(0);
+ Map<String, Set<String>> missing = result.get(1);
+
+ // Print all dependences found in the format:
+ // +Found: <FQCN from zip>
+ // uses: FQCN
+
+ mLog.info("++++++ %d Entries found in source JARs", deps.size());
+ mLog.info("");
+
+ for (Entry<String, Set<String>> entry : deps.entrySet()) {
+ mLog.info( "+Found : %s", entry.getKey());
+ for (String dep : entry.getValue()) {
+ mLog.info(" uses: %s", dep);
+ }
+
+ mLog.info("");
+ }
+
+
+ // Now print all missing dependences in the format:
+ // -Missing <FQCN>:
+ // used by: <FQCN>
+
+ mLog.info("");
+ mLog.info("------ %d Entries missing from source JARs", missing.size());
+ mLog.info("");
+
+ for (Entry<String, Set<String>> entry : missing.entrySet()) {
+ mLog.info( "-Missing : %s", entry.getKey());
+ for (String dep : entry.getValue()) {
+ mLog.info(" used by: %s", dep);
+ }
+
+ mLog.info("");
+ }
+ }
+
+ /**
+ * Prints only a summary of the missing dependencies to the current logger.
+ */
+ public void printMissingDeps(List<Map<String, Set<String>>> result) {
+ assert result.size() == 2;
+ @SuppressWarnings("unused") Map<String, Set<String>> deps = result.get(0);
+ Map<String, Set<String>> missing = result.get(1);
+
+ for (String fqcn : missing.keySet()) {
+ mLog.info("%s", fqcn);
+ }
+ }
+
+ // ----------------
+
+ /**
+ * Parses a JAR file and returns a list of all classes founds using a map
+ * class name => ASM ClassReader. Class names are in the form "android.view.View".
+ */
+ Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
+ TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+ for (String jarPath : jarPathList) {
+ ZipFile zip = new ZipFile(jarPath);
+ Enumeration<? extends ZipEntry> entries = zip.entries();
+ ZipEntry entry;
+ while (entries.hasMoreElements()) {
+ entry = entries.nextElement();
+ if (entry.getName().endsWith(".class")) {
+ ClassReader cr = new ClassReader(zip.getInputStream(entry));
+ String className = classReaderToClassName(cr);
+ classes.put(className, cr);
+ }
+ }
+ }
+
+ return classes;
+ }
+
+ /**
+ * Utility that returns the fully qualified binary class name for a ClassReader.
+ * E.g. it returns something like android.view.View.
+ */
+ static String classReaderToClassName(ClassReader classReader) {
+ if (classReader == null) {
+ return null;
+ } else {
+ return classReader.getClassName().replace('/', '.');
+ }
+ }
+
+ /**
+ * Utility that returns the fully qualified binary class name from a path-like FQCN.
+ * E.g. it returns android.view.View from android/view/View.
+ */
+ static String internalToBinaryClassName(String className) {
+ if (className == null) {
+ return null;
+ } else {
+ return className.replace('/', '.');
+ }
+ }
+
+ /**
+ * Finds all dependencies for all classes in keepClasses which are also
+ * listed in zipClasses. Returns a map of all the dependencies found.
+ */
+ Map<String, Set<String>> findClassesDeps(Map<String, ClassReader> zipClasses) {
+
+ // The dependencies that we'll collect.
+ // It's a map Class name => uses class names.
+ Map<String, Set<String>> dependencyMap = new TreeMap<String, Set<String>>();
+
+ DependencyVisitor visitor = getVisitor();
+
+ int count = 0;
+ try {
+ for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+ String name = entry.getKey();
+
+ TreeSet<String> set = new TreeSet<String>();
+ dependencyMap.put(name, set);
+ visitor.setDependencySet(set);
+
+ ClassReader cr = entry.getValue();
+ cr.accept(visitor, 0 /* flags */);
+
+ visitor.setDependencySet(null);
+
+ mLog.debugNoln("Visited %d classes\r", ++count);
+ }
+ } finally {
+ mLog.debugNoln("\n");
+ }
+
+ return dependencyMap;
+ }
+
+ /**
+ * Computes which classes FQCN were found as dependencies that are NOT listed
+ * in the original JAR classes.
+ *
+ * @param deps The map { FQCN => dependencies[] } returned by {@link #findClassesDeps(Map)}.
+ * @param zipClasses The set of all classes FQCN found in the JAR files.
+ * @return A map { FQCN not found in the zipClasses => classes using it }
+ */
+ private Map<String, Set<String>> findMissingClasses(
+ Map<String, Set<String>> deps,
+ Set<String> zipClasses) {
+ Map<String, Set<String>> missing = new TreeMap<String, Set<String>>();
+
+ for (Entry<String, Set<String>> entry : deps.entrySet()) {
+ String name = entry.getKey();
+
+ for (String dep : entry.getValue()) {
+ if (!zipClasses.contains(dep)) {
+ // This dependency doesn't exist in the zip classes.
+ Set<String> set = missing.get(dep);
+ if (set == null) {
+ set = new TreeSet<String>();
+ missing.put(dep, set);
+ }
+ set.add(name);
+ }
+ }
+
+ }
+
+ return missing;
+ }
+
+
+ // ----------------------------------
+
+ /**
+ * Instantiates a new DependencyVisitor. Useful for unit tests.
+ */
+ @VisibleForTesting(visibility=Visibility.PRIVATE)
+ DependencyVisitor getVisitor() {
+ return new DependencyVisitor();
+ }
+
+ /**
+ * Visitor to collect all the type dependencies from a class.
+ */
+ public class DependencyVisitor extends ClassVisitor {
+
+ private Set<String> mCurrentDepSet;
+
+ /**
+ * Creates a new visitor that will find all the dependencies for the visited class.
+ */
+ public DependencyVisitor() {
+ super(Opcodes.ASM4);
+ }
+
+ /**
+ * Sets the {@link Set} where to record direct dependencies for this class.
+ * This will change before each {@link ClassReader#accept(ClassVisitor, int)} call.
+ */
+ public void setDependencySet(Set<String> set) {
+ mCurrentDepSet = set;
+ }
+
+ /**
+ * Considers the given class name as a dependency.
+ */
+ public void considerName(String className) {
+ if (className == null) {
+ return;
+ }
+
+ className = internalToBinaryClassName(className);
+
+ try {
+ // exclude classes that are part of the default JRE (the one executing this program)
+ if (getClass().getClassLoader().loadClass(className) != null) {
+ return;
+ }
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+
+ // Add it to the dependency set for the currently visited class, as needed.
+ assert mCurrentDepSet != null;
+ if (mCurrentDepSet != null) {
+ mCurrentDepSet.add(className);
+ }
+ }
+
+ /**
+ * Considers this array of names using considerName().
+ */
+ public void considerNames(String[] classNames) {
+ if (classNames != null) {
+ for (String className : classNames) {
+ considerName(className);
+ }
+ }
+ }
+
+ /**
+ * Considers this signature or type signature by invoking the {@link SignatureVisitor}
+ * on it.
+ */
+ public void considerSignature(String signature) {
+ if (signature != null) {
+ SignatureReader sr = new SignatureReader(signature);
+ // SignatureReader.accept will call accessType so we don't really have
+ // to differentiate where the signature comes from.
+ sr.accept(new MySignatureVisitor());
+ }
+ }
+
+ /**
+ * Considers this {@link Type}. For arrays, the element type is considered.
+ * If the type is an object, it's internal name is considered.
+ */
+ public void considerType(Type t) {
+ if (t != null) {
+ if (t.getSort() == Type.ARRAY) {
+ t = t.getElementType();
+ }
+ if (t.getSort() == Type.OBJECT) {
+ considerName(t.getInternalName());
+ }
+ }
+ }
+
+ /**
+ * Considers a descriptor string. The descriptor is converted to a {@link Type}
+ * and then considerType() is invoked.
+ */
+ public boolean considerDesc(String desc) {
+ if (desc != null) {
+ try {
+ if (desc.length() > 0 && desc.charAt(0) == '(') {
+ // This is a method descriptor with arguments and a return type.
+ Type t = Type.getReturnType(desc);
+ considerType(t);
+
+ for (Type arg : Type.getArgumentTypes(desc)) {
+ considerType(arg);
+ }
+
+ } else {
+ Type t = Type.getType(desc);
+ considerType(t);
+ }
+ return true;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // ignore, not a valid type.
+ }
+ }
+ return false;
+ }
+
+
+ // ---------------------------------------------------
+ // --- ClassVisitor, FieldVisitor
+ // ---------------------------------------------------
+
+ // Visits a class header
+ @Override
+ public void visit(int version, int access, String name,
+ String signature, String superName, String[] interfaces) {
+ // signature is the signature of this class. May be null if the class is not a generic
+ // one, and does not extend or implement generic classes or interfaces.
+
+ if (signature != null) {
+ considerSignature(signature);
+ }
+
+ // superName is the internal of name of the super class (see getInternalName).
+ // For interfaces, the super class is Object. May be null but only for the Object class.
+ considerName(superName);
+
+ // interfaces is the internal names of the class's interfaces (see getInternalName).
+ // May be null.
+ considerNames(interfaces);
+ }
+
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ // desc is the class descriptor of the annotation class.
+ considerDesc(desc);
+ return new MyAnnotationVisitor();
+ }
+
+ @Override
+ public void visitAttribute(Attribute attr) {
+ // pass
+ }
+
+ // Visits the end of a class
+ @Override
+ public void visitEnd() {
+ // pass
+ }
+
+ private class MyFieldVisitor extends FieldVisitor {
+
+ public MyFieldVisitor() {
+ super(Opcodes.ASM4);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ // desc is the class descriptor of the annotation class.
+ considerDesc(desc);
+ return new MyAnnotationVisitor();
+ }
+
+ @Override
+ public void visitAttribute(Attribute attr) {
+ // pass
+ }
+
+ // Visits the end of a class
+ @Override
+ public void visitEnd() {
+ // pass
+ }
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ // desc is the field's descriptor (see Type).
+ considerDesc(desc);
+
+ // signature is the field's signature. May be null if the field's type does not use
+ // generic types.
+ considerSignature(signature);
+
+ return new MyFieldVisitor();
+ }
+
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ // name is the internal name of an inner class (see getInternalName).
+ // Note: outerName/innerName seems to be null when we're reading the
+ // _Original_ClassName classes generated by layoutlib_create.
+ if (outerName != null) {
+ considerName(name);
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ // desc is the method's descriptor (see Type).
+ considerDesc(desc);
+ // signature is the method's signature. May be null if the method parameters, return
+ // type and exceptions do not use generic types.
+ considerSignature(signature);
+
+ return new MyMethodVisitor();
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ // pass
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ // pass
+ }
+
+
+ // ---------------------------------------------------
+ // --- MethodVisitor
+ // ---------------------------------------------------
+
+ private class MyMethodVisitor extends MethodVisitor {
+
+ public MyMethodVisitor() {
+ super(Opcodes.ASM4);
+ }
+
+
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ return new MyAnnotationVisitor();
+ }
+
+ @Override
+ public void visitCode() {
+ // pass
+ }
+
+ // field instruction
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ // name is the field's name.
+ // desc is the field's descriptor (see Type).
+ considerDesc(desc);
+ }
+
+ @Override
+ public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+ // pass
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment) {
+ // pass -- an IINC instruction
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ // pass -- a zero operand instruction
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ // pass -- a single int operand instruction
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ // pass -- a jump instruction
+ }
+
+ @Override
+ public void visitLabel(Label label) {
+ // pass -- a label target
+ }
+
+ // instruction to load a constant from the stack
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (cst instanceof Type) {
+ considerType((Type) cst);
+ }
+ }
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ // pass
+ }
+
+ @Override
+ public void visitLocalVariable(String name, String desc,
+ String signature, Label start, Label end, int index) {
+ // desc is the type descriptor of this local variable.
+ considerDesc(desc);
+ // signature is the type signature of this local variable. May be null if the local
+ // variable type does not use generic types.
+ considerSignature(signature);
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ // pass -- a lookup switch instruction
+ }
+
+ @Override
+ public void visitMaxs(int maxStack, int maxLocals) {
+ // pass
+ }
+
+ // instruction that invokes a method
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+
+ // owner is the internal name of the method's owner class
+ if (!considerDesc(owner) && owner.indexOf('/') != -1) {
+ considerName(owner);
+ }
+ // desc is the method's descriptor (see Type).
+ considerDesc(desc);
+ }
+
+ // instruction multianewarray, whatever that is
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+
+ // desc an array type descriptor.
+ considerDesc(desc);
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+ boolean visible) {
+ // desc is the class descriptor of the annotation class.
+ considerDesc(desc);
+ return new MyAnnotationVisitor();
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+ // pass -- table switch instruction
+
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ // type is the internal name of the type of exceptions handled by the handler,
+ // or null to catch any exceptions (for "finally" blocks).
+ considerName(type);
+ }
+
+ // type instruction
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ // type is the operand of the instruction to be visited. This operand must be the
+ // internal name of an object or array class.
+ considerName(type);
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ // pass -- local variable instruction
+ }
+ }
+
+ private class MySignatureVisitor extends SignatureVisitor {
+
+ public MySignatureVisitor() {
+ super(Opcodes.ASM4);
+ }
+
+ // ---------------------------------------------------
+ // --- SignatureVisitor
+ // ---------------------------------------------------
+
+ private String mCurrentSignatureClass = null;
+
+ // Starts the visit of a signature corresponding to a class or interface type
+ @Override
+ public void visitClassType(String name) {
+ mCurrentSignatureClass = name;
+ considerName(name);
+ }
+
+ // Visits an inner class
+ @Override
+ public void visitInnerClassType(String name) {
+ if (mCurrentSignatureClass != null) {
+ mCurrentSignatureClass += "$" + name;
+ considerName(mCurrentSignatureClass);
+ }
+ }
+
+ @Override
+ public SignatureVisitor visitArrayType() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public void visitBaseType(char descriptor) {
+ // pass -- a primitive type, ignored
+ }
+
+ @Override
+ public SignatureVisitor visitClassBound() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public SignatureVisitor visitExceptionType() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public void visitFormalTypeParameter(String name) {
+ // pass
+ }
+
+ @Override
+ public SignatureVisitor visitInterface() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public SignatureVisitor visitInterfaceBound() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public SignatureVisitor visitParameterType() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public SignatureVisitor visitReturnType() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public SignatureVisitor visitSuperclass() {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public SignatureVisitor visitTypeArgument(char wildcard) {
+ return new MySignatureVisitor();
+ }
+
+ @Override
+ public void visitTypeVariable(String name) {
+ // pass
+ }
+
+ @Override
+ public void visitTypeArgument() {
+ // pass
+ }
+ }
+
+
+ // ---------------------------------------------------
+ // --- AnnotationVisitor
+ // ---------------------------------------------------
+
+ private class MyAnnotationVisitor extends AnnotationVisitor {
+
+ public MyAnnotationVisitor() {
+ super(Opcodes.ASM4);
+ }
+
+ // Visits a primitive value of an annotation
+ @Override
+ public void visit(String name, Object value) {
+ // value is the actual value, whose type must be Byte, Boolean, Character, Short,
+ // Integer, Long, Float, Double, String or Type
+ if (value instanceof Type) {
+ considerType((Type) value);
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ // desc is the class descriptor of the nested annotation class.
+ considerDesc(desc);
+ return new MyAnnotationVisitor();
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ return new MyAnnotationVisitor();
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ // desc is the class descriptor of the enumeration class.
+ considerDesc(desc);
+ }
+ }
+ }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
index 8efd871..c3ba591 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
@@ -33,11 +33,19 @@
}
}
+ /** Similar to debug() but doesn't do a \n automatically. */
+ public void debugNoln(String format, Object... args) {
+ if (mVerbose) {
+ String s = String.format(format, args);
+ System.out.print(s);
+ }
+ }
+
public void info(String format, Object... args) {
String s = String.format(format, args);
outPrintln(s);
}
-
+
public void error(String format, Object... args) {
String s = String.format(format, args);
errPrintln(s);
@@ -50,15 +58,15 @@
pw.flush();
error(format + "\n" + sw.toString(), args);
}
-
+
/** for unit testing */
protected void errPrintln(String msg) {
System.err.println(msg);
}
-
+
/** for unit testing */
protected void outPrintln(String msg) {
System.out.println(msg);
}
-
+
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index 4b7a348..9cd74db 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -18,6 +18,8 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
@@ -47,6 +49,8 @@
public static class Options {
public boolean generatePublicAccess = true;
+ public boolean listAllDeps = false;
+ public boolean listOnlyMissingDeps = false;
}
public static final Options sOptions = new Options();
@@ -60,16 +64,29 @@
if (!processArgs(log, args, osJarPath, osDestJar)) {
log.error("Usage: layoutlib_create [-v] [-p] output.jar input.jar ...");
+ log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ...");
System.exit(1);
}
- log.info("Output: %1$s", osDestJar[0]);
+ if (sOptions.listAllDeps || sOptions.listOnlyMissingDeps) {
+ System.exit(listDeps(osJarPath, log));
+
+ } else {
+ System.exit(createLayoutLib(osDestJar[0], osJarPath, log));
+ }
+
+
+ System.exit(1);
+ }
+
+ private static int createLayoutLib(String osDestJar, ArrayList<String> osJarPath, Log log) {
+ log.info("Output: %1$s", osDestJar);
for (String path : osJarPath) {
log.info("Input : %1$s", path);
}
try {
- AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new CreateInfo());
+ AsmGenerator agen = new AsmGenerator(log, osDestJar, new CreateInfo());
AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
new String[] { // derived from
@@ -116,17 +133,33 @@
for (String path : osJarPath) {
log.info("- Input JAR : %1$s", path);
}
- System.exit(1);
+ return 1;
}
- System.exit(0);
+ return 0;
} catch (IOException e) {
log.exception(e, "Failed to load jar");
} catch (LogAbortException e) {
e.error(log);
}
- System.exit(1);
+ return 1;
+ }
+
+ private static int listDeps(ArrayList<String> osJarPath, Log log) {
+ DependencyFinder df = new DependencyFinder(log);
+ try {
+ List<Map<String, Set<String>>> result = df.findDeps(osJarPath);
+ if (sOptions.listAllDeps) {
+ df.printAllDeps(result);
+ } else if (sOptions.listOnlyMissingDeps) {
+ df.printMissingDeps(result);
+ }
+ } catch (IOException e) {
+ log.exception(e, "Failed to load jar");
+ }
+
+ return 0;
}
/**
@@ -138,14 +171,21 @@
*/
private static boolean processArgs(Log log, String[] args,
ArrayList<String> osJarPath, String[] osDestJar) {
+ boolean needs_dest = true;
for (int i = 0; i < args.length; i++) {
String s = args[i];
if (s.equals("-v")) {
log.setVerbose(true);
} else if (s.equals("-p")) {
sOptions.generatePublicAccess = false;
+ } else if (s.equals("--list-deps")) {
+ sOptions.listAllDeps = true;
+ needs_dest = false;
+ } else if (s.equals("--missing-deps")) {
+ sOptions.listOnlyMissingDeps = true;
+ needs_dest = false;
} else if (!s.startsWith("-")) {
- if (osDestJar[0] == null) {
+ if (needs_dest && osDestJar[0] == null) {
osDestJar[0] = s;
} else {
osJarPath.add(s);
@@ -160,7 +200,7 @@
log.error("Missing parameter: path to input jar");
return false;
}
- if (osDestJar[0] == null) {
+ if (needs_dest && osDestJar[0] == null) {
log.error("Missing parameter: path to output jar");
return false;
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 7f8f9ce..23b1b44 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -697,7 +697,7 @@
setInitialState(mInitialState);
- setProcessedMessagesSize(100);
+ setLogRecSize(100);
if (DBG) setDbg(true);
//start the state machine
@@ -1151,7 +1151,7 @@
}
@Override
- protected boolean recordProcessedMessage(Message msg) {
+ protected boolean recordLogRec(Message msg) {
//Ignore screen on/off & common messages when driver has started
if (getCurrentState() == mConnectedState || getCurrentState() == mDisconnectedState) {
switch (msg.what) {
@@ -1651,7 +1651,7 @@
handlePostDhcpSetup();
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
- mDhcpStateMachine.quit();
+ mDhcpStateMachine.doQuit();
mDhcpStateMachine = null;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 9e004d0..5759074 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -1196,7 +1196,7 @@
} else {
if (DBG) logd("stop DHCP client");
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
- mDhcpStateMachine.quit();
+ mDhcpStateMachine.doQuit();
mDhcpStateMachine = null;
}