diff options
109 files changed, 3550 insertions, 1827 deletions
diff --git a/Android.mk b/Android.mk index 568352ea796b..7520afe87133 100644 --- a/Android.mk +++ b/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES += \  	core/java/android/os/IParentalControlCallback.aidl \  	core/java/android/os/IPermissionController.aidl \  	core/java/android/os/IPowerManager.aidl \ +    core/java/android/os/IRemoteCallback.aidl \  	core/java/android/os/IVibratorService.aidl \      core/java/android/service/wallpaper/IWallpaperConnection.aidl \      core/java/android/service/wallpaper/IWallpaperEngine.aidl \ @@ -136,6 +137,7 @@ LOCAL_SRC_FILES += \  	core/java/android/speech/tts/ITtsCallback.aidl \  	core/java/com/android/internal/app/IBatteryStats.aidl \  	core/java/com/android/internal/app/IUsageStats.aidl \ +	core/java/com/android/internal/app/IMediaContainerService.aidl \  	core/java/com/android/internal/appwidget/IAppWidgetService.aidl \  	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \  	core/java/com/android/internal/backup/IBackupTransport.aidl \ diff --git a/api/current.xml b/api/current.xml index cd200c143d68..12504612860a 100644 --- a/api/current.xml +++ b/api/current.xml @@ -3184,7 +3184,7 @@   value="16843395"   static="true"   final="true" - deprecated="deprecated" + deprecated="not deprecated"   visibility="public"  >  </field> @@ -3261,7 +3261,7 @@   value="16843362"   static="true"   final="true" - deprecated="deprecated" + deprecated="not deprecated"   visibility="public"  >  </field> @@ -5131,7 +5131,7 @@   value="16842997"   static="true"   final="true" - deprecated="deprecated" + deprecated="not deprecated"   visibility="public"  >  </field> @@ -5252,7 +5252,7 @@   value="16842996"   static="true"   final="true" - deprecated="deprecated" + deprecated="not deprecated"   visibility="public"  >  </field> @@ -16176,11 +16176,26 @@   synchronized="false"   static="false"   final="false" + deprecated="deprecated" + visibility="protected" +> +<parameter name="id" type="int"> +</parameter> +</method> +<method name="onCreateDialog" + return="android.app.Dialog" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false"   deprecated="not deprecated"   visibility="protected"  >  <parameter name="id" type="int">  </parameter> +<parameter name="args" type="android.os.Bundle"> +</parameter>  </method>  <method name="onCreateOptionsMenu"   return="boolean" @@ -16476,6 +16491,21 @@   synchronized="false"   static="false"   final="false" + deprecated="deprecated" + visibility="protected" +> +<parameter name="id" type="int"> +</parameter> +<parameter name="dialog" type="android.app.Dialog"> +</parameter> +</method> +<method name="onPrepareDialog" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false"   deprecated="not deprecated"   visibility="protected"  > @@ -16483,6 +16513,8 @@  </parameter>  <parameter name="dialog" type="android.app.Dialog">  </parameter> +<parameter name="args" type="android.os.Bundle"> +</parameter>  </method>  <method name="onPrepareOptionsMenu"   return="boolean" @@ -17110,6 +17142,21 @@  <parameter name="id" type="int">  </parameter>  </method> +<method name="showDialog" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="int"> +</parameter> +<parameter name="args" type="android.os.Bundle"> +</parameter> +</method>  <method name="startActivityForResult"   return="void"   abstract="false" @@ -19895,6 +19942,21 @@  <parameter name="context" type="android.content.Context">  </parameter>  </method> +<method name="onDisableRequested" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +</method>  <method name="onDisabled"   return="void"   abstract="false" @@ -19996,6 +20058,17 @@   visibility="public"  >  </field> +<field name="ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field>  <field name="ACTION_DEVICE_ADMIN_ENABLED"   type="java.lang.String"   transient="false" @@ -20051,6 +20124,17 @@   visibility="public"  >  </field> +<field name="EXTRA_DISABLE_WARNING" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.app.extra.DISABLE_WARNING"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field>  </class>  <class name="DeviceAdminInfo"   extends="java.lang.Object" @@ -20420,6 +20504,21 @@  <parameter name="password" type="java.lang.String">  </parameter>  </method> +<method name="setMaximumFailedPasswordsForWipe" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="num" type="int"> +</parameter> +</method>  <method name="setMaximumTimeToLock"   return="void"   abstract="false" @@ -20500,6 +20599,17 @@   visibility="public"  >  </field> +<field name="EXTRA_ADD_EXPLANATION" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.app.extra.ADD_EXPLANATION"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field>  <field name="EXTRA_DEVICE_ADMIN"   type="java.lang.String"   transient="false" @@ -119960,6 +120070,28 @@   visibility="public"  >  </field> +<field name="EXTRA_HEADERS_KEY" + type="java.lang.String" + transient="false" + volatile="false" + value=""com.android.browser.headers_key"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_HEADERS_VALUE" + type="java.lang.String" + transient="false" + volatile="false" + value=""com.android.browser.headers_value"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field>  <field name="HISTORY_PROJECTION"   type="java.lang.String[]"   transient="false" @@ -189850,6 +189982,21 @@  >  <parameter name="url" type="java.lang.String">  </parameter> +<parameter name="extraHeaders" type="java.util.Map<java.lang.String, java.lang.String>"> +</parameter> +</method> +<method name="loadUrl" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="url" type="java.lang.String"> +</parameter>  </method>  <method name="onChildViewAdded"   return="void" diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index a8e217e4e510..81d60dc5da39 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -1191,14 +1191,6 @@ status_t CameraService::Client::setParameters(const String8& params)      CameraParameters p(params); -    // The orientation parameter is actually for CameraService, not for the camera driver. -    if (p.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { -        LOGV("portrait mode"); -        mOrientation = ISurface::BufferHeap::ROT_90; -    } else { -        mOrientation = 0; -    } -      return mHardware->setParameters(p);  } @@ -1224,6 +1216,30 @@ status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t a      status_t result = checkPid();      if (result != NO_ERROR) return result; +    if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { +        // The orientation cannot be set during preview. +        if (mHardware->previewEnabled()) { +            return INVALID_OPERATION; +        } +        switch (arg1) { +            case 0: +                mOrientation = ISurface::BufferHeap::ROT_0; +                break; +            case 90: +                mOrientation = ISurface::BufferHeap::ROT_90; +                break; +            case 180: +                mOrientation = ISurface::BufferHeap::ROT_180; +                break; +            case 270: +                mOrientation = ISurface::BufferHeap::ROT_270; +                break; +            default: +                return BAD_VALUE; +        } +        return OK; +    } +      if (mHardware == 0) {          LOGE("mHardware is NULL, returning.");          return INVALID_OPERATION; diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 9300915fa458..78f90a1172fb 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -129,7 +129,7 @@ static void dumpstate() {      dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");      dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads"); -    printf("----- BACKLIGHTS -----\n"); +    printf("------ BACKLIGHTS ------\n");      printf("LCD brightness=");      dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");      printf("Button brightness="); diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c index fda618cc4c52..39e14e4be5f4 100644 --- a/cmds/dumpstate/utils.c +++ b/cmds/dumpstate/utils.c @@ -43,13 +43,13 @@ int dump_file(const char *title, const char* path) {      int fd = open(path, O_RDONLY);      if (fd < 0) {          int err = errno; -        if (title) printf("----- %s (%s) -----\n", title, path); +        if (title) printf("------ %s (%s) ------\n", title, path);          printf("*** %s: %s\n", path, strerror(err));          if (title) printf("\n");          return -1;      } -    if (title) printf("----- %s (%s", title, path); +    if (title) printf("------ %s (%s", title, path);      if (title) {          struct stat st; @@ -59,7 +59,7 @@ int dump_file(const char *title, const char* path) {              strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));              printf(": %s", stamp);          } -        printf(") -----\n"); +        printf(") ------\n");      }      int newline = 0; @@ -97,13 +97,13 @@ int run_command(const char *title, int timeout_seconds, const char *command, ...          va_list ap;          va_start(ap, command); -        if (title) printf("----- %s (%s", title, command); +        if (title) printf("------ %s (%s", title, command);          for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {              args[arg] = va_arg(ap, const char *);              if (args[arg] == NULL) break;              if (title) printf(" %s", args[arg]);          } -        if (title) printf(") -----\n"); +        if (title) printf(") ------\n");          fflush(stdout);          execvp(command, (char**) args); @@ -159,7 +159,7 @@ void print_properties() {      property_list(print_prop, NULL);      qsort(&props, num_props, sizeof(props[0]), compare_prop); -    printf("----- SYSTEM PROPERTIES -----\n"); +    printf("------ SYSTEM PROPERTIES ------\n");      for (i = 0; i < num_props; ++i) {          fputs(props[i], stdout);          free(props[i]); diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c index 5db554594746..555c19e07aa9 100644 --- a/cmds/installd/utils.c +++ b/cmds/installd/utils.c @@ -33,6 +33,7 @@ int create_pkg_path(char path[PKG_PATH_MAX],      }      x = pkgname; +    int alpha = -1;      while (*x) {          if (isalnum(*x) || (*x == '_')) {                  /* alphanumeric or underscore are fine */ @@ -42,13 +43,28 @@ int create_pkg_path(char path[PKG_PATH_MAX],                  LOGE("invalid package name '%s'\n", pkgname);                  return -1;              } -        } else { +        } else if (*x == '-') { +            /* Suffix -X is fine to let versioning of packages. +               But whatever follows should be alphanumeric.*/ +            alpha = 1; +        }else {                  /* anything not A-Z, a-z, 0-9, _, or . is invalid */              LOGE("invalid package name '%s'\n", pkgname);              return -1;          }          x++;      } +    if (alpha == 1) { +        // Skip current character +        x++; +        while (*x) { +            if (!isalnum(*x)) { +                LOGE("invalid package name '%s' should include only numbers after -\n", pkgname); +                return -1; +            } +            x++; +        } +    }      sprintf(path, "%s%s%s", prefix, pkgname, postfix);      return 0; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 1c3414d14438..95142e380ba2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -39,6 +39,7 @@ import android.os.Build;  import android.os.Bundle;  import android.os.Handler;  import android.os.IBinder; +import android.os.Looper;  import android.os.RemoteException;  import android.text.Selection;  import android.text.SpannableStringBuilder; @@ -608,8 +609,13 @@ public class Activity extends ContextThemeWrapper      private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";      private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";      private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_"; +    private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_"; -    private SparseArray<Dialog> mManagedDialogs; +    private static class ManagedDialog { +        Dialog mDialog; +        Bundle mArgs; +    } +    private SparseArray<ManagedDialog> mManagedDialogs;      // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called.      private Instrumentation mInstrumentation; @@ -850,35 +856,41 @@ public class Activity extends ContextThemeWrapper          final int[] ids = b.getIntArray(SAVED_DIALOG_IDS_KEY);          final int numDialogs = ids.length; -        mManagedDialogs = new SparseArray<Dialog>(numDialogs); +        mManagedDialogs = new SparseArray<ManagedDialog>(numDialogs);          for (int i = 0; i < numDialogs; i++) {              final Integer dialogId = ids[i];              Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId));              if (dialogState != null) {                  // Calling onRestoreInstanceState() below will invoke dispatchOnCreate                  // so tell createDialog() not to do it, otherwise we get an exception -                final Dialog dialog = createDialog(dialogId, dialogState); -                mManagedDialogs.put(dialogId, dialog); -                onPrepareDialog(dialogId, dialog); -                dialog.onRestoreInstanceState(dialogState); +                final ManagedDialog md = new ManagedDialog(); +                md.mArgs = b.getBundle(savedDialogArgsKeyFor(dialogId)); +                md.mDialog = createDialog(dialogId, dialogState, md.mArgs); +                if (md.mDialog != null) { +                    mManagedDialogs.put(dialogId, md); +                    onPrepareDialog(dialogId, md.mDialog, md.mArgs); +                    md.mDialog.onRestoreInstanceState(dialogState); +                }              }          }      } -    private Dialog createDialog(Integer dialogId, Bundle state) { -        final Dialog dialog = onCreateDialog(dialogId); +    private Dialog createDialog(Integer dialogId, Bundle state, Bundle args) { +        final Dialog dialog = onCreateDialog(dialogId, args);          if (dialog == null) { -            throw new IllegalArgumentException("Activity#onCreateDialog did " -                    + "not create a dialog for id " + dialogId); +            return null;          }          dialog.dispatchOnCreate(state);          return dialog;      } -    private String savedDialogKeyFor(int key) { +    private static String savedDialogKeyFor(int key) {          return SAVED_DIALOG_KEY_PREFIX + key;      } +    private static String savedDialogArgsKeyFor(int key) { +        return SAVED_DIALOG_ARGS_KEY_PREFIX + key; +    }      /**       * Called when activity start-up is complete (after {@link #onStart} @@ -1095,8 +1107,11 @@ public class Activity extends ContextThemeWrapper          for (int i = 0; i < numDialogs; i++) {              final int key = mManagedDialogs.keyAt(i);              ids[i] = key; -            final Dialog dialog = mManagedDialogs.valueAt(i); -            dialogState.putBundle(savedDialogKeyFor(key), dialog.onSaveInstanceState()); +            final ManagedDialog md = mManagedDialogs.valueAt(i); +            dialogState.putBundle(savedDialogKeyFor(key), md.mDialog.onSaveInstanceState()); +            if (md.mArgs != null) { +                dialogState.putBundle(savedDialogArgsKeyFor(key), md.mArgs); +            }          }          dialogState.putIntArray(SAVED_DIALOG_IDS_KEY, ids); @@ -1282,14 +1297,14 @@ public class Activity extends ContextThemeWrapper          // dismiss any dialogs we are managing.          if (mManagedDialogs != null) { -              final int numDialogs = mManagedDialogs.size();              for (int i = 0; i < numDialogs; i++) { -                final Dialog dialog = mManagedDialogs.valueAt(i); -                if (dialog.isShowing()) { -                    dialog.dismiss(); +                final ManagedDialog md = mManagedDialogs.valueAt(i); +                if (md.mDialog.isShowing()) { +                    md.mDialog.dismiss();                  }              } +            mManagedDialogs = null;          }          // close any cursors we are managing. @@ -1300,6 +1315,7 @@ public class Activity extends ContextThemeWrapper                  c.mCursor.close();              }          } +        mManagedCursors.clear();      }      /** @@ -2410,36 +2426,57 @@ public class Activity extends ContextThemeWrapper      }      /** +     * @deprecated Old no-arguments version of {@link #onCreateDialog(int, Bundle)}. +     */ +    @Deprecated +    protected Dialog onCreateDialog(int id) { +        return null; +    } + +    /**       * Callback for creating dialogs that are managed (saved and restored) for you -     * by the activity. +     * by the activity.  The default implementation calls through to +     * {@link #onCreateDialog(int)} for compatibility.       * -     * If you use {@link #showDialog(int)}, the activity will call through to +     * <p>If you use {@link #showDialog(int)}, the activity will call through to       * this method the first time, and hang onto it thereafter.  Any dialog       * that is created by this method will automatically be saved and restored       * for you, including whether it is showing.       * -     * If you would like the activity to manage the saving and restoring dialogs +     * <p>If you would like the activity to manage saving and restoring dialogs       * for you, you should override this method and handle any ids that are       * passed to {@link #showDialog}.       * -     * If you would like an opportunity to prepare your dialog before it is shown, -     * override {@link #onPrepareDialog(int, Dialog)}. +     * <p>If you would like an opportunity to prepare your dialog before it is shown, +     * override {@link #onPrepareDialog(int, Dialog, Bundle)}.       *       * @param id The id of the dialog. -     * @return The dialog +     * @param args The dialog arguments provided to {@link #showDialog(int, Bundle)}. +     * @return The dialog.  If you return null, the dialog will not be created.       * -     * @see #onPrepareDialog(int, Dialog) -     * @see #showDialog(int) +     * @see #onPrepareDialog(int, Dialog, Bundle) +     * @see #showDialog(int, Bundle)       * @see #dismissDialog(int)       * @see #removeDialog(int)       */ -    protected Dialog onCreateDialog(int id) { -        return null; +    protected Dialog onCreateDialog(int id, Bundle args) { +        return onCreateDialog(id); +    } + +    /** +     * @deprecated Old no-arguments version of +     * {@link #onPrepareDialog(int, Dialog, Bundle)}. +     */ +    @Deprecated +    protected void onPrepareDialog(int id, Dialog dialog) { +        dialog.setOwnerActivity(this);      }      /**       * Provides an opportunity to prepare a managed dialog before it is being -     * shown. +     * shown.  The default implementation calls through to +     * {@link #onPrepareDialog(int, Dialog)} for compatibility. +     *        * <p>       * Override this if you need to update a managed dialog based on the state       * of the application each time it is shown. For example, a time picker @@ -2449,43 +2486,66 @@ public class Activity extends ContextThemeWrapper       *        * @param id The id of the managed dialog.       * @param dialog The dialog. -     * @see #onCreateDialog(int) +     * @param args The dialog arguments provided to {@link #showDialog(int, Bundle)}. +     * @see #onCreateDialog(int, Bundle)       * @see #showDialog(int)       * @see #dismissDialog(int)       * @see #removeDialog(int)       */ -    protected void onPrepareDialog(int id, Dialog dialog) { -        dialog.setOwnerActivity(this); +    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { +        onPrepareDialog(id, dialog); +    } + +    /** +     * Simple version of {@link #showDialog(int, Bundle)} that does not +     * take any arguments.  Simply calls {@link #showDialog(int, Bundle)} +     * with null arguments. +     */ +    public final void showDialog(int id) { +        showDialog(id, null);      }      /** -     * Show a dialog managed by this activity.  A call to {@link #onCreateDialog(int)} +     * Show a dialog managed by this activity.  A call to {@link #onCreateDialog(int, Bundle)}       * will be made with the same id the first time this is called for a given       * id.  From thereafter, the dialog will be automatically saved and restored.       * -     * Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog)} will +     * <p>Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog, Bundle)} will       * be made to provide an opportunity to do any timely preparation.       *       * @param id The id of the managed dialog. -     * +     * @param args Arguments to pass through to the dialog.  These will be saved +     * and restored for you.  Note that if the dialog is already created, +     * {@link #onCreateDialog(int, Bundle)} will not be called with the new +     * arguments but {@link #onPrepareDialog(int, Dialog, Bundle)} will be. +     * If you need to rebuild the dialog, call {@link #removeDialog(int)}Êfirst. +     * @return Returns true if the Dialog was created; false is returned if +     * it is not created because {@link #onCreateDialog(int, Bundle)} returns false. +     *        * @see Dialog -     * @see #onCreateDialog(int) -     * @see #onPrepareDialog(int, Dialog) +     * @see #onCreateDialog(int, Bundle) +     * @see #onPrepareDialog(int, Dialog, Bundle)       * @see #dismissDialog(int)       * @see #removeDialog(int)       */ -    public final void showDialog(int id) { +    public final boolean showDialog(int id, Bundle args) {          if (mManagedDialogs == null) { -            mManagedDialogs = new SparseArray<Dialog>(); +            mManagedDialogs = new SparseArray<ManagedDialog>();          } -        Dialog dialog = mManagedDialogs.get(id); -        if (dialog == null) { -            dialog = createDialog(id, null); -            mManagedDialogs.put(id, dialog); +        ManagedDialog md = mManagedDialogs.get(id); +        if (md == null) { +            md = new ManagedDialog(); +            md.mDialog = createDialog(id, null, args); +            if (md.mDialog == null) { +                return false; +            } +            mManagedDialogs.put(id, md);          } -        onPrepareDialog(id, dialog); -        dialog.show(); +        md.mArgs = args; +        onPrepareDialog(id, md.mDialog, args); +        md.mDialog.show(); +        return true;      }      /** @@ -2496,21 +2556,21 @@ public class Activity extends ContextThemeWrapper       * @throws IllegalArgumentException if the id was not previously shown via       *   {@link #showDialog(int)}.       * -     * @see #onCreateDialog(int) -     * @see #onPrepareDialog(int, Dialog) +     * @see #onCreateDialog(int, Bundle) +     * @see #onPrepareDialog(int, Dialog, Bundle)       * @see #showDialog(int)       * @see #removeDialog(int)       */      public final void dismissDialog(int id) {          if (mManagedDialogs == null) {              throw missingDialog(id); -          } -        final Dialog dialog = mManagedDialogs.get(id); -        if (dialog == null) { +         +        final ManagedDialog md = mManagedDialogs.get(id); +        if (md == null) {              throw missingDialog(id);          } -        dialog.dismiss(); +        md.mDialog.dismiss();      }      /** @@ -2526,28 +2586,27 @@ public class Activity extends ContextThemeWrapper       * Removes any internal references to a dialog managed by this Activity.       * If the dialog is showing, it will dismiss it as part of the clean up.       * -     * This can be useful if you know that you will never show a dialog again and +     * <p>This can be useful if you know that you will never show a dialog again and       * want to avoid the overhead of saving and restoring it in the future.       *       * @param id The id of the managed dialog.       * -     * @see #onCreateDialog(int) -     * @see #onPrepareDialog(int, Dialog) +     * @see #onCreateDialog(int, Bundle) +     * @see #onPrepareDialog(int, Dialog, Bundle)       * @see #showDialog(int)       * @see #dismissDialog(int)       */      public final void removeDialog(int id) { -          if (mManagedDialogs == null) {              return;          } -        final Dialog dialog = mManagedDialogs.get(id); -        if (dialog == null) { +        final ManagedDialog md = mManagedDialogs.get(id); +        if (md == null) {              return;          } -        dialog.dismiss(); +        md.mDialog.dismiss();          mManagedDialogs.remove(id);      } @@ -3449,17 +3508,7 @@ public class Activity extends ContextThemeWrapper              return;          } -        // uses super.getSystemService() since this.getSystemService() looks at the -        // mSearchManager field. -        mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE); -        int ident = mIdent; -        if (ident == 0) { -            if (mParent != null) ident = mParent.mIdent; -            if (ident == 0) { -                throw new IllegalArgumentException("no ident"); -            } -        } -        mSearchManager.setIdent(ident, getComponentName()); +        mSearchManager = new SearchManager(this, null);      }      @Override diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java index 9750d6ed7e6c..88fdab23134a 100644 --- a/core/java/android/app/DeviceAdmin.java +++ b/core/java/android/app/DeviceAdmin.java @@ -22,6 +22,7 @@ import android.content.BroadcastReceiver;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; +import android.os.Bundle;  /**   * Base class for implementing a device administration component.  This @@ -62,6 +63,27 @@ public class DeviceAdmin extends BroadcastReceiver {              = "android.app.action.DEVICE_ADMIN_ENABLED";      /** +     * Action sent to a device administrator when the user has requested to +     * disable it, but before this has actually been done.  This gives you +     * a chance to supply a message to the user about the impact of +     * disabling your admin, by setting the extra field +     * {@link #EXTRA_DISABLE_WARNING} in the result Intent.  If not set, +     * no warning will be displayed.  If set, the given text will be shown +     * to the user before they disable your admin. +     */ +    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) +    public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED +            = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"; +     +    /** +     * A CharSequence that can be shown to the user informing them of the +     * impact of disabling your admin. +     * +     * @see #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED +     */ +    public static final String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING"; +     +    /**       * Action sent to a device administrator when the user has disabled       * it.  Upon return, the application no longer has access to the       * protected device policy manager APIs.  You will generally @@ -166,6 +188,21 @@ public class DeviceAdmin extends BroadcastReceiver {      }      /** +     * Called when the user has asked to disable the administrator, as a result of +     * receiving {@link #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED}, giving you +     * a chance to present a warning message to them.  The message is returned +     * as the result; if null is returned (the default implementation), no +     * message will be displayed. +     * @param context The running context as per {@link #onReceive}. +     * @param intent The received intent as per {@link #onReceive}. +     * @return Return the warning message to display to the user before +     * being disabled; if null is returned, no message is displayed. +     */ +    public CharSequence onDisableRequested(Context context, Intent intent) { +        return null; +    } +     +    /**       * Called prior to the administrator being disabled, as a result of       * receiving {@link #ACTION_DEVICE_ADMIN_DISABLED}.  Upon return, you       * can no longer use the protected parts of the {@link DevicePolicyManager} @@ -226,6 +263,12 @@ public class DeviceAdmin extends BroadcastReceiver {              onPasswordSucceeded(context, intent);          } else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {              onEnabled(context, intent); +        } else if (ACTION_DEVICE_ADMIN_DISABLE_REQUESTED.equals(action)) { +            CharSequence res = onDisableRequested(context, intent); +            if (res != null) { +                Bundle extras = getResultExtras(true); +                extras.putCharSequence(EXTRA_DISABLE_WARNING, res); +            }          } else if (ACTION_DEVICE_ADMIN_DISABLED.equals(action)) {              onDisabled(context, intent);          } diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java index 92fdbc80908f..e50db89e8ec4 100644 --- a/core/java/android/app/DeviceAdminInfo.java +++ b/core/java/android/app/DeviceAdminInfo.java @@ -19,7 +19,6 @@ package android.app;  import org.xmlpull.v1.XmlPullParser;  import org.xmlpull.v1.XmlPullParserException; -import android.R;  import android.content.ComponentName;  import android.content.Context;  import android.content.pm.ActivityInfo; @@ -111,48 +110,47 @@ public final class DeviceAdminInfo implements Parcelable {      /** @hide */      public static class PolicyInfo { +        public final int ident;          final public String tag;          final public int label;          final public int description; -        public PolicyInfo(String tagIn, int labelIn, int descriptionIn) { +        public PolicyInfo(int identIn, String tagIn, int labelIn, int descriptionIn) { +            ident = identIn;              tag = tagIn;              label = labelIn;              description = descriptionIn;          }      } +    static ArrayList<PolicyInfo> sPoliciesDisplayOrder = new ArrayList<PolicyInfo>();      static HashMap<String, Integer> sKnownPolicies = new HashMap<String, Integer>();      static SparseArray<PolicyInfo> sRevKnownPolicies = new SparseArray<PolicyInfo>();      static { -        sRevKnownPolicies.put(USES_POLICY_LIMIT_PASSWORD, -                new PolicyInfo("limit-password", -                        com.android.internal.R.string.policylab_limitPassword, -                        com.android.internal.R.string.policydesc_limitPassword)); -        sRevKnownPolicies.put(USES_POLICY_WATCH_LOGIN, -                new PolicyInfo("watch-login", -                        com.android.internal.R.string.policylab_watchLogin, -                        com.android.internal.R.string.policydesc_watchLogin)); -        sRevKnownPolicies.put(USES_POLICY_RESET_PASSWORD, -                new PolicyInfo("reset-password", -                        com.android.internal.R.string.policylab_resetPassword, -                        com.android.internal.R.string.policydesc_resetPassword)); -        sRevKnownPolicies.put(USES_POLICY_LIMIT_UNLOCK, -                new PolicyInfo("limit-unlock", -                        com.android.internal.R.string.policylab_limitUnlock, -                        com.android.internal.R.string.policydesc_limitUnlock)); -        sRevKnownPolicies.put(USES_POLICY_FORCE_LOCK, -                new PolicyInfo("force-lock", -                        com.android.internal.R.string.policylab_forceLock, -                        com.android.internal.R.string.policydesc_forceLock)); -        sRevKnownPolicies.put(USES_POLICY_WIPE_DATA, -                new PolicyInfo("wipe-data", -                        com.android.internal.R.string.policylab_wipeData, -                        com.android.internal.R.string.policydesc_wipeData)); -        for (int i=0; i<sRevKnownPolicies.size(); i++) { -            sKnownPolicies.put(sRevKnownPolicies.valueAt(i).tag, -                    sRevKnownPolicies.keyAt(i)); +        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data", +                com.android.internal.R.string.policylab_wipeData, +                com.android.internal.R.string.policydesc_wipeData)); +        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_RESET_PASSWORD, "reset-password", +                com.android.internal.R.string.policylab_resetPassword, +                com.android.internal.R.string.policydesc_resetPassword)); +        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_LIMIT_PASSWORD, "limit-password", +                com.android.internal.R.string.policylab_limitPassword, +                com.android.internal.R.string.policydesc_limitPassword)); +        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WATCH_LOGIN, "watch-login", +                com.android.internal.R.string.policylab_watchLogin, +                com.android.internal.R.string.policydesc_watchLogin)); +        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_LIMIT_UNLOCK, "limit-unlock", +                com.android.internal.R.string.policylab_limitUnlock, +                com.android.internal.R.string.policydesc_limitUnlock)); +        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_FORCE_LOCK, "force-lock", +                com.android.internal.R.string.policylab_forceLock, +                com.android.internal.R.string.policydesc_forceLock)); +         +        for (int i=0; i<sPoliciesDisplayOrder.size(); i++) { +            PolicyInfo pi = sPoliciesDisplayOrder.get(i); +            sRevKnownPolicies.put(pi.ident, pi); +            sKnownPolicies.put(pi.tag, pi.ident);          }      } @@ -335,10 +333,10 @@ public final class DeviceAdminInfo implements Parcelable {      /** @hide */      public ArrayList<PolicyInfo> getUsedPolicies() {          ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>(); -        for (int i=0; i<sRevKnownPolicies.size(); i++) { -            int ident = sRevKnownPolicies.keyAt(i); -            if (usesPolicy(ident)) { -                res.add(sRevKnownPolicies.valueAt(i)); +        for (int i=0; i<sPoliciesDisplayOrder.size(); i++) { +            PolicyInfo pi = sPoliciesDisplayOrder.get(i); +            if (usesPolicy(pi.ident)) { +                res.add(pi);              }          }          return res; diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java index 25e323080a71..9de7336d2bba 100644 --- a/core/java/android/app/DevicePolicyManager.java +++ b/core/java/android/app/DevicePolicyManager.java @@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo;  import android.content.pm.PackageManager;  import android.content.pm.ResolveInfo;  import android.os.Handler; +import android.os.RemoteCallback;  import android.os.RemoteException;  import android.os.ServiceManager;  import android.util.Log; @@ -43,8 +44,9 @@ public class DevicePolicyManager {      private static boolean localLOGV = DEBUG || android.util.Config.LOGV;      private final Context mContext; -    private final Handler mHandler;      private final IDevicePolicyManager mService; +     +    private final Handler mHandler;      /*package*/ DevicePolicyManager(Context context, Handler handler) {          mContext = context; @@ -60,6 +62,10 @@ public class DevicePolicyManager {       * bring the user through adding the device administrator to the system (or       * allowing them to reject it).       *  +     * <p>You can optionally include the {@link #EXTRA_ADD_EXPLANATION} +     * field to provide the user with additional explanation (in addition +     * to your component's description) about what is being added. +     *        * <p>Note: the current platform can only have one device administrator       * active at a time.  If you make this request while there is already       * an active administrator, this new request will be canceled automatically. @@ -76,6 +82,14 @@ public class DevicePolicyManager {      public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";      /** +     * An optional CharSequence providing additional explanation for why the +     * admin is being added. +     * +     * @see #ACTION_ADD_DEVICE_ADMIN +     */ +    public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION"; +     +    /**       * Activity action: have the user enter a new password.  This activity       * should be launched after using {@link #setPasswordMode(ComponentName, int)}       * or {@link #setMinimumPasswordLength(ComponentName, int)} to have the @@ -285,6 +299,29 @@ public class DevicePolicyManager {      }      /** +     * Set the maximum number of failed password attempts that are allowed +     * before the device wipes its data.  This is convenience for implementing +     * the corresponding functionality with a combination of watching failed +     * password attempts and calling {@link #wipeData} upon reaching a certain +     * count, and as such requires that you request both +     * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and +     * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}. +     *  +     * @param admin Which {@link DeviceAdmin} this request is associated with. +     * @param num The number of failed password attempts at which point the +     * device will wipe its data. +     */ +    public void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) { +        if (mService != null) { +            try { +                mService.setMaximumFailedPasswordsForWipe(admin, num); +            } catch (RemoteException e) { +                Log.w(TAG, "Failed talking with device policy service", e); +            } +        } +    } +     +    /**       * Force a new password on the user.  This takes effect immediately.  The       * given password must meet the current password minimum length constraint       * or it will be rejected.  The given password will be accepted regardless @@ -451,6 +488,19 @@ public class DevicePolicyManager {      /**       * @hide       */ +    public void getRemoveWarning(ComponentName admin, RemoteCallback result) { +        if (mService != null) { +            try { +                mService.getRemoveWarning(admin, result); +            } catch (RemoteException e) { +                Log.w(TAG, "Failed talking with device policy service", e); +            } +        } +    } + +    /** +     * @hide +     */      public void setActivePasswordState(int mode, int length) {          if (mService != null) {              try { diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl index 7e38194807bd..edb8603554ee 100644 --- a/core/java/android/app/IDevicePolicyManager.aidl +++ b/core/java/android/app/IDevicePolicyManager.aidl @@ -18,6 +18,7 @@  package android.app;  import android.content.ComponentName; +import android.os.RemoteCallback;  /**   * Internal IPC interface to the device policy service. @@ -32,6 +33,7 @@ interface IDevicePolicyManager {      boolean isActivePasswordSufficient();      int getCurrentFailedPasswordAttempts(); +    void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);      boolean resetPassword(String password); @@ -44,6 +46,7 @@ interface IDevicePolicyManager {      void setActiveAdmin(in ComponentName policyReceiver);      ComponentName getActiveAdmin(); +    void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);      void removeActiveAdmin(in ComponentName policyReceiver);      void setActivePasswordState(int mode, int length); diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 0920467d8b89..9ba7863b4d3b 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -29,23 +29,4 @@ interface ISearchManager {     List<SearchableInfo> getSearchablesForWebSearch();     SearchableInfo getDefaultSearchableForWebSearch();     void setDefaultWebSearch(in ComponentName component); -   void startSearch(in String initialQuery, -            boolean selectInitialQuery, -            in ComponentName launchActivity, -            in Bundle appSearchData, -            boolean globalSearch, -            ISearchManagerCallback searchManagerCallback, -            int ident); - -    void triggerSearch(in String query, -            in ComponentName launchActivity, -            in Bundle appSearchData, -            ISearchManagerCallback searchManagerCallback, -            int ident); - -    void stopSearch(); - - -    boolean isVisible(); -  } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index b3963968b09a..3dfbe7184f90 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -73,8 +73,8 @@ import java.util.WeakHashMap;  import java.util.concurrent.atomic.AtomicLong;  /** - * System search dialog. This is controlled by the  - * SearchManagerService and runs in the system process. + * Search dialog. This is controlled by the  + * SearchManager and runs in the current foreground process.   *    * @hide   */ @@ -118,6 +118,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS      private Bundle mAppSearchData;      private boolean mGlobalSearchMode;      private Context mActivityContext; +    private SearchManager mSearchManager;      // Values we store to allow user to toggle between in-app search and global search.      private ComponentName mStoredComponentName; @@ -157,7 +158,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS       *        * @param context Application Context we can use for system acess       */ -    public SearchDialog(Context context) { +    public SearchDialog(Context context, SearchManager searchManager) {          super(context, com.android.internal.R.style.Theme_GlobalSearchBar);          // Save voice intent for later queries/launching @@ -168,6 +169,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS          mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);          mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +        mSearchManager = searchManager;      }      /** @@ -180,7 +182,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS          Window theWindow = getWindow();          WindowManager.LayoutParams lp = theWindow.getAttributes(); -        lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR;          lp.width = ViewGroup.LayoutParams.MATCH_PARENT;          // taking up the whole window (even when transparent) is less than ideal,          // but necessary to show the popup window until the window manager supports @@ -291,8 +292,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS              //              // TODO: When the browser icon issue is reconciled in Eclair, remove this special case.              if (isBrowserSearch()) currentSearchText = ""; -             -            return doShow(currentSearchText, false, null, mAppSearchData, true); + +            cancel(); +            mSearchManager.startGlobalSearch(currentSearchText, false, mStoredAppSearchData); +            return true;          } else {              if (mStoredComponentName != null) {                  // This means we should toggle *back* to an in-app search context from @@ -1314,8 +1317,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS      }      /** -     * Launches an intent, including any special intent handling.  Doesn't dismiss the dialog -     * since that will be handled in {@link SearchDialogWrapper#performActivityResuming} +     * Launches an intent, including any special intent handling.       */      private void launchIntent(Intent intent) {          if (intent == null) { diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index d25d6702c703..12a434784620 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1709,7 +1709,7 @@ public class SearchManager      /* package */ OnDismissListener mDismissListener = null;      /* package */ OnCancelListener mCancelListener = null; -    private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback(); +    private SearchDialog mSearchDialog;      /*package*/ SearchManager(Context context, Handler handler)  {          mContext = context; @@ -1778,31 +1778,29 @@ public class SearchManager                              ComponentName launchActivity,                              Bundle appSearchData,                              boolean globalSearch) { -        if (mIdent == 0) throw new IllegalArgumentException( -                "Called from outside of an Activity context"); +        ensureSearchDialog();          if (globalSearch) {              startGlobalSearch(initialQuery, selectInitialQuery, appSearchData);              return;          } -        if (!mAssociatedPackage.equals(launchActivity.getPackageName())) { -            Log.w(TAG, "invoking app search on a different package " + -                    "not associated with this search manager"); -        } -        try { -            // activate the search manager and start it up! -            mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, -                    globalSearch, mSearchManagerCallback, mIdent); -        } catch (RemoteException ex) { -            Log.e(TAG, "startSearch() failed.", ex); +        mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, +                globalSearch); +    } + +    private void ensureSearchDialog() { +        if (mSearchDialog == null) { +            mSearchDialog = new SearchDialog(mContext, this); +            mSearchDialog.setOnCancelListener(this); +            mSearchDialog.setOnDismissListener(this);          }      }      /**       * Starts the global search activity.       */ -    private void startGlobalSearch(String initialQuery, boolean selectInitialQuery, +    /* package */ void startGlobalSearch(String initialQuery, boolean selectInitialQuery,              Bundle appSearchData) {          ComponentName globalSearchActivity = getGlobalSearchActivity();          if (globalSearchActivity == null) { @@ -1876,8 +1874,6 @@ public class SearchManager      public void triggerSearch(String query,                                ComponentName launchActivity,                                Bundle appSearchData) { -        if (mIdent == 0) throw new IllegalArgumentException( -                "Called from outside of an Activity context");          if (!mAssociatedPackage.equals(launchActivity.getPackageName())) {              throw new IllegalArgumentException("invoking app search on a different package " +                      "not associated with this search manager"); @@ -1886,12 +1882,8 @@ public class SearchManager              Log.w(TAG, "triggerSearch called with empty query, ignoring.");              return;          } -        try { -            mService.triggerSearch(query, launchActivity, appSearchData, mSearchManagerCallback, -                    mIdent); -        } catch (RemoteException ex) { -            Log.e(TAG, "triggerSearch() failed.", ex); -        } +        startSearch(query, false, launchActivity, appSearchData, false); +        mSearchDialog.launchQuerySearch();      }      /** @@ -1906,10 +1898,8 @@ public class SearchManager       * @see #startSearch       */      public void stopSearch() { -        if (DBG) debug("stopSearch()"); -        try { -            mService.stopSearch(); -        } catch (RemoteException ex) { +        if (mSearchDialog != null) { +            mSearchDialog.cancel();          }      } @@ -1923,13 +1913,7 @@ public class SearchManager       * @hide       */      public boolean isVisible() { -        if (DBG) debug("isVisible()"); -        try { -            return mService.isVisible(); -        } catch (RemoteException e) { -            Log.e(TAG, "isVisible() failed: " + e); -            return false; -        } +        return mSearchDialog == null? false : mSearchDialog.isShowing();      }      /** @@ -1976,44 +1960,14 @@ public class SearchManager          mCancelListener = listener;      } -    private class SearchManagerCallback extends ISearchManagerCallback.Stub { - -        private final Runnable mFireOnDismiss = new Runnable() { -            public void run() { -                if (DBG) debug("mFireOnDismiss"); -                if (mDismissListener != null) { -                    mDismissListener.onDismiss(); -                } -            } -        }; - -        private final Runnable mFireOnCancel = new Runnable() { -            public void run() { -                if (DBG) debug("mFireOnCancel"); -                if (mCancelListener != null) { -                    mCancelListener.onCancel(); -                } -            } -        }; - -        public void onDismiss() { -            if (DBG) debug("onDismiss()"); -            mHandler.post(mFireOnDismiss); -        } - -        public void onCancel() { -            if (DBG) debug("onCancel()"); -            mHandler.post(mFireOnCancel); -        } - -    } -      /**       * @deprecated This method is an obsolete internal implementation detail. Do not use.       */      @Deprecated      public void onCancel(DialogInterface dialog) { -        throw new UnsupportedOperationException(); +        if (mCancelListener != null) { +            mCancelListener.onCancel(); +        }      }      /** @@ -2021,7 +1975,9 @@ public class SearchManager       */      @Deprecated      public void onDismiss(DialogInterface dialog) { -        throw new UnsupportedOperationException(); +        if (mDismissListener != null) { +            mDismissListener.onDismiss(); +        }      }      /** @@ -2208,4 +2164,4 @@ public class SearchManager          Thread thread = Thread.currentThread();          Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");      } -}
\ No newline at end of file +} diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index d90536ca97e4..4c4455a4c516 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -543,6 +543,21 @@ public class Camera {      public native final void stopSmoothZoom();      /** +     * Set the display orientation. This affects the preview frames and the +     * picture displayed after snapshot. This method is useful for portrait +     * mode applications. +     * +     * This does not affect the order of byte array passed in +     * {@link PreviewCallback#onPreviewFrame}. This method is not allowed to +     * be called during preview. +     * +     * @param degrees the angle that the picture will be rotated clockwise. +     *                Valid values are 0, 90, 180, and 270. +     * @hide +     */ +    public native final void setDisplayOrientation(int degrees); + +    /**       * Handles the zoom callback.       *       * @hide diff --git a/core/java/android/os/IRemoteCallback.aidl b/core/java/android/os/IRemoteCallback.aidl new file mode 100644 index 000000000000..f0c6c73ee8fd --- /dev/null +++ b/core/java/android/os/IRemoteCallback.aidl @@ -0,0 +1,25 @@ +/* //device/java/android/android/app/IActivityPendingResult.aidl +** +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License");  +** you may not use this file except in compliance with the License.  +** You may obtain a copy of the License at  +** +**     http://www.apache.org/licenses/LICENSE-2.0  +** +** Unless required by applicable law or agreed to in writing, software  +** distributed under the License is distributed on an "AS IS" BASIS,  +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  +** See the License for the specific language governing permissions and  +** limitations under the License. +*/ + +package android.os; + +import android.os.Bundle; + +/** @hide */ +oneway interface IRemoteCallback { +    void sendResult(in Bundle data); +} diff --git a/core/java/android/os/Messenger.java b/core/java/android/os/Messenger.java index 1bc554e03c16..ad55abdde7ed 100644 --- a/core/java/android/os/Messenger.java +++ b/core/java/android/os/Messenger.java @@ -29,7 +29,7 @@ public final class Messenger implements Parcelable {       * Create a new Messenger pointing to the given Handler.  Any Message       * objects sent through this Messenger will appear in the Handler as if       * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had -     * be called directly. +     * been called directly.       *        * @param target The Handler that will receive sent messages.       */ diff --git a/core/java/android/os/RemoteCallback.aidl b/core/java/android/os/RemoteCallback.aidl new file mode 100644 index 000000000000..7ae56f57401c --- /dev/null +++ b/core/java/android/os/RemoteCallback.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License");  +** you may not use this file except in compliance with the License.  +** You may obtain a copy of the License at  +** +**     http://www.apache.org/licenses/LICENSE-2.0  +** +** Unless required by applicable law or agreed to in writing, software  +** distributed under the License is distributed on an "AS IS" BASIS,  +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  +** See the License for the specific language governing permissions and  +** limitations under the License. +*/ + +package android.os; + +parcelable RemoteCallback; diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java new file mode 100644 index 000000000000..ca95bdf529cd --- /dev/null +++ b/core/java/android/os/RemoteCallback.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * TODO: Make this a public API?  Let's see how it goes with a few use + * cases first. + * @hide + */ +public abstract class RemoteCallback implements Parcelable { +    final Handler mHandler; +    final IRemoteCallback mTarget; +     +    class DeliverResult implements Runnable { +        final Bundle mResult; +         +        DeliverResult(Bundle result) { +            mResult = result; +        } +         +        public void run() { +            onResult(mResult); +        } +    } +     +    class LocalCallback extends IRemoteCallback.Stub { +        public void sendResult(Bundle bundle) { +            mHandler.post(new DeliverResult(bundle)); +        } +    } +     +    static class RemoteCallbackProxy extends RemoteCallback { +        RemoteCallbackProxy(IRemoteCallback target) { +            super(target); +        } +         +        protected void onResult(Bundle bundle) { +        } +    } +     +    public RemoteCallback(Handler handler) { +        mHandler = handler; +        mTarget = new LocalCallback(); +    } +     +     RemoteCallback(IRemoteCallback target) { +        mHandler = null; +        mTarget = target; +    } +     +    public void sendResult(Bundle bundle) throws RemoteException { +        mTarget.sendResult(bundle); +    } +     +    protected abstract void onResult(Bundle bundle); +     +    public boolean equals(Object otherObj) { +        if (otherObj == null) { +            return false; +        } +        try { +            return mTarget.asBinder().equals(((RemoteCallback)otherObj) +                    .mTarget.asBinder()); +        } catch (ClassCastException e) { +        } +        return false; +    } +     +    public int hashCode() { +        return mTarget.asBinder().hashCode(); +    } +     +    public int describeContents() { +        return 0; +    } + +    public void writeToParcel(Parcel out, int flags) { +        out.writeStrongBinder(mTarget.asBinder()); +    } + +    public static final Parcelable.Creator<RemoteCallback> CREATOR +            = new Parcelable.Creator<RemoteCallback>() { +        public RemoteCallback createFromParcel(Parcel in) { +            IBinder target = in.readStrongBinder(); +            return target != null ? new RemoteCallbackProxy( +                    IRemoteCallback.Stub.asInterface(target)) : null; +        } + +        public RemoteCallback[] newArray(int size) { +            return new RemoteCallback[size]; +        } +    }; +} diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index d67169f6e51d..ef43afe183df 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -34,12 +34,6 @@ public class Browser {          Uri.parse("content://browser/bookmarks");      /** -     * The inline scheme to show embedded content in a browser. -     * @hide -     */ -    public static final Uri INLINE_URI = Uri.parse("inline:"); - -    /**       * The name of extra data when starting Browser with ACTION_VIEW or       * ACTION_SEARCH intent.       * <p> @@ -62,24 +56,6 @@ public class Browser {          "com.android.browser.application_id";      /** -     * The content to be rendered when url's scheme is inline. -     * @hide -     */ -    public static final String EXTRA_INLINE_CONTENT ="com.android.browser.inline.content"; - -    /** -     * The encoding of the inlined content for inline scheme. -     * @hide -     */ -    public static final String EXTRA_INLINE_ENCODING ="com.android.browser.inline.encoding"; - -    /** -     * The url used when the inline content is falied to render. -     * @hide -     */ -    public static final String EXTRA_INLINE_FAILURL ="com.android.browser.inline.failurl"; - -    /**       * The name of the extra data in the VIEW intent. The data is in boolean.       * <p>       * If the Browser is handling the intent and the setting for @@ -102,6 +78,27 @@ public class Browser {       */      public static final String EXTRA_POST_DATA = "com.android.browser.post_data"; +    /** +     * The name of the extra data in the VIEW intent. The data is in the format +     * of String array. This should be paired with EXTRA_HEADERS_VALUE. +     * <p> +     * The keys will be combined with the values and sent in the HTTP request +     * headers for the provided url. The keys can't be the standard HTTP headers +     * as they are set by the WebView. The url's schema must be http(s). +     * <p> +     */ +    public static final String EXTRA_HEADERS_KEY = "com.android.browser.headers_key"; + +    /** +     * The name of the extra data in the VIEW intent. The data is in the format +     * of String array. This should be paired with EXTRA_HEADERS_KEY. +     * <p> +     * The values will be combined with the keys and sent in the HTTP request +     * headers for the provided url. The url's schema must be http(s). +     * <p> +     */ +    public static final String EXTRA_HEADERS_VALUE = "com.android.browser.headers_value"; +      /* if you change column order you must also change indices         below */      public static final String[] HISTORY_PROJECTION = new String[] { diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index cbd8a264c6c0..f89ba9195ead 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -744,9 +744,10 @@ public final class Calendar {                  try {                      while (subCursor.moveToNext()) {                          ContentValues extendedValues = new ContentValues(); -                        extendedValues.put(ExtendedProperties.NAME, cursor.getString(COLUMN_NAME)); +                        extendedValues.put(ExtendedProperties.NAME, +                                subCursor.getString(COLUMN_NAME));                          extendedValues.put(ExtendedProperties.VALUE, -                                cursor.getString(COLUMN_VALUE)); +                                subCursor.getString(COLUMN_VALUE));                          entity.addSubValue(ExtendedProperties.CONTENT_URI, extendedValues);                      }                  } finally { diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 6bf09b592528..08ab166c6571 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -517,6 +517,8 @@ public final class Downloads {      /**       * This download couldn't be completed because of a storage issue.       * Typically, that's because the filesystem is missing or full. +     * Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR} +     * and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.       * @hide       */      public static final int STATUS_FILE_ERROR = 492; @@ -558,6 +560,21 @@ public final class Downloads {      public static final int STATUS_TOO_MANY_REDIRECTS = 497;      /** +     * This download couldn't be completed due to insufficient storage +     * space.  Typically, this is because the SD card is full. +     * @hide +     */ +    public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498; + +    /** +     * This download couldn't be completed because no external storage +     * device was found.  Typically, this is because the SD card is not +     * mounted. +     * @hide +     */ +    public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499; + +    /**       * This download is visible but only shows in the notifications       * while it's in progress.       * @hide @@ -1019,6 +1036,8 @@ public final class Downloads {          /**           * This download couldn't be completed because of a storage issue.           * Typically, that's because the filesystem is missing or full. +         * Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR} +         * and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.           */          public static final int STATUS_FILE_ERROR = 492; @@ -1054,6 +1073,19 @@ public final class Downloads {          public static final int STATUS_TOO_MANY_REDIRECTS = 497;          /** +         * This download couldn't be completed due to insufficient storage +         * space.  Typically, this is because the SD card is full. +         */ +        public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498; + +        /** +         * This download couldn't be completed because no external storage +         * device was found.  Typically, this is because the SD card is not +         * mounted. +         */ +        public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499; + +        /**           * This download is visible but only shows in the notifications           * while it's in progress.           */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ecfea0000e87..23a9f496e6a2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2033,10 +2033,10 @@ public final class Settings {          public static final String ALLOW_MOCK_LOCATION = "mock_location";          /** -         * The Android ID (a unique 64-bit value) as a hex string. -         * Identical to that obtained by calling -         * GoogleLoginService.getAndroidId(); it is also placed here -         * so you can get it without binding to a service. +         * A 64-bit number (as a hex string) that is randomly +         * generated on the device's first boot and should remain +         * constant for the lifetime of the device.  (The value may +         * change if a factory reset is performed on the device.)           */          public static final String ANDROID_ID = "android_id"; diff --git a/core/java/android/server/search/SearchDialogWrapper.java b/core/java/android/server/search/SearchDialogWrapper.java deleted file mode 100644 index 9ee64af367d5..000000000000 --- a/core/java/android/server/search/SearchDialogWrapper.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.search; - -import android.app.ISearchManagerCallback; -import android.app.SearchDialog; -import android.app.SearchManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.os.DeadObjectException; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.text.TextUtils; -import android.util.Log; - -/** - * Runs an instance of {@link SearchDialog} on its own thread. - */ -class SearchDialogWrapper -implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { - -    private static final String TAG = "SearchManagerService"; -    private static final boolean DBG = false; - -    private static final String SEARCH_UI_THREAD_NAME = "SearchDialog"; -    private static final int SEARCH_UI_THREAD_PRIORITY = -        android.os.Process.THREAD_PRIORITY_DEFAULT; - -    // Takes no arguments -    private static final int MSG_INIT = 0; -    // Takes these arguments: -    // arg1: selectInitialQuery, 0 = false, 1 = true -    // arg2: globalSearch, 0 = false, 1 = true -    // obj: searchManagerCallback -    // data[KEY_INITIAL_QUERY]: initial query -    // data[KEY_LAUNCH_ACTIVITY]: launch activity -    // data[KEY_APP_SEARCH_DATA]: app search data -    // data[KEY_TRIGGER]: 0 = false, 1 = true -    private static final int MSG_START_SEARCH = 1; -    // Takes no arguments -    private static final int MSG_STOP_SEARCH = 2; -    // arg1 is activity id -    private static final int MSG_ACTIVITY_RESUMING = 3; -    // obj is the reason -    private static final int MSG_CLOSING_SYSTEM_DIALOGS = 4; - -    private static final String KEY_INITIAL_QUERY = "q"; -    private static final String KEY_LAUNCH_ACTIVITY = "a"; -    private static final String KEY_APP_SEARCH_DATA = "d"; -    private static final String KEY_IDENT = "i"; -    private static final String KEY_TRIGGER = "t"; - -    // Context used for getting search UI resources -    private final Context mContext; - -    // Handles messages on the search UI thread. -    private final SearchDialogHandler mSearchUiThread; - -    // The search UI -    SearchDialog mSearchDialog; - -    // If the search UI is visible, this is the callback for the client that showed it. -    ISearchManagerCallback mCallback = null; - -    // Identity of last activity that started search. -    private int mStartedIdent = 0; -     -    // Identity of currently resumed activity. -    private int mResumedIdent = 0; - -    // True if we have registered our receivers. -    private boolean mReceiverRegistered; - -    private volatile boolean mVisible = false; -     -    /** -     * Creates a new search dialog wrapper and a search UI thread. The search dialog itself will -     * be created some asynchronously on the search UI thread. -     * -     * @param context Context used for getting search UI resources. -     */ -    public SearchDialogWrapper(Context context) { -        mContext = context; - -        // Create the search UI thread -        HandlerThread t = new HandlerThread(SEARCH_UI_THREAD_NAME, SEARCH_UI_THREAD_PRIORITY); -        t.start(); -        mSearchUiThread = new SearchDialogHandler(t.getLooper()); - -        // Create search UI on the search UI thread -        mSearchUiThread.sendEmptyMessage(MSG_INIT); -    } - -    public boolean isVisible() { -        return mVisible; -    } - -    /** -     * Initializes the search UI. -     * Must be called from the search UI thread. -     */ -    private void init() { -        mSearchDialog = new SearchDialog(mContext); -        mSearchDialog.setOnCancelListener(this); -        mSearchDialog.setOnDismissListener(this); -    } - -    private void registerBroadcastReceiver() { -        if (!mReceiverRegistered) { -            IntentFilter filter = new IntentFilter( -                    Intent.ACTION_CONFIGURATION_CHANGED); -            mContext.registerReceiver(mBroadcastReceiver, filter, null, -                    mSearchUiThread); -            mReceiverRegistered = true; -        } -    } - -    private void unregisterBroadcastReceiver() { -        if (mReceiverRegistered) { -            mContext.unregisterReceiver(mBroadcastReceiver); -            mReceiverRegistered = false; -        } -    } - -    /** -     * Closes the search dialog when requested by the system (e.g. when a phone call comes in). -     */ -    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { -        @Override -        public void onReceive(Context context, Intent intent) { -            String action = intent.getAction(); -            if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { -                if (DBG) debug(Intent.ACTION_CONFIGURATION_CHANGED); -                performOnConfigurationChanged(); -            } -        } -    }; - -    // -    // External API -    // - -    /** -     * Launches the search UI. -     * Can be called from any thread. -     * -     * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) -     */ -    public void startSearch(final String initialQuery, -            final boolean selectInitialQuery, -            final ComponentName launchActivity, -            final Bundle appSearchData, -            final boolean globalSearch, -            final ISearchManagerCallback searchManagerCallback, -            int ident, -            boolean trigger) { -        if (DBG) debug("startSearch()"); -        Message msg = Message.obtain(); -        msg.what = MSG_START_SEARCH; -        msg.arg1 = selectInitialQuery ? 1 : 0; -        msg.arg2 = globalSearch ? 1 : 0; -        msg.obj = searchManagerCallback; -        Bundle msgData = msg.getData(); -        msgData.putString(KEY_INITIAL_QUERY, initialQuery); -        msgData.putParcelable(KEY_LAUNCH_ACTIVITY, launchActivity); -        msgData.putBundle(KEY_APP_SEARCH_DATA, appSearchData); -        msgData.putInt(KEY_IDENT, ident); -        msgData.putInt(KEY_TRIGGER, trigger ? 1 : 0); -        mSearchUiThread.sendMessage(msg); -        // be a little more eager in setting this so isVisible will return the correct value if -        // called immediately after startSearch -        mVisible = true; -    } - -    /** -     * Cancels the search dialog. -     * Can be called from any thread. -     */ -    public void stopSearch() { -        if (DBG) debug("stopSearch()"); -        mSearchUiThread.sendEmptyMessage(MSG_STOP_SEARCH); -        // be a little more eager in setting this so isVisible will return the correct value if -        // called immediately after stopSearch -        mVisible = false; -    } - -    /** -     * Updates the currently resumed activity. -     * Can be called from any thread. -     */ -    public void activityResuming(int ident) { -        if (DBG) debug("activityResuming(ident=" + ident + ")"); -        Message msg = Message.obtain(); -        msg.what = MSG_ACTIVITY_RESUMING; -        msg.arg1 = ident; -        mSearchUiThread.sendMessage(msg); -    } - -    /** -     * Handles closing of system windows/dialogs -     * Can be called from any thread. -     */ -    public void closingSystemDialogs(String reason) { -        if (DBG) debug("closingSystemDialogs(reason=" + reason + ")"); -        Message msg = Message.obtain(); -        msg.what = MSG_CLOSING_SYSTEM_DIALOGS; -        msg.obj = reason; -        mSearchUiThread.sendMessage(msg); -    } - -    // -    // Implementation methods that run on the search UI thread -    // - -    private class SearchDialogHandler extends Handler { - -        public SearchDialogHandler(Looper looper) { -            super(looper); -        } - -        @Override -        public void handleMessage(Message msg) { -            switch (msg.what) { -                case MSG_INIT: -                    init(); -                    break; -                case MSG_START_SEARCH: -                    handleStartSearchMessage(msg); -                    break; -                case MSG_STOP_SEARCH: -                    performStopSearch(); -                    break; -                case MSG_ACTIVITY_RESUMING: -                    performActivityResuming(msg.arg1); -                    break; -                case MSG_CLOSING_SYSTEM_DIALOGS: -                    performClosingSystemDialogs((String)msg.obj); -                    break; -            } -        } - -        private void handleStartSearchMessage(Message msg) { -            Bundle msgData = msg.getData(); -            String initialQuery = msgData.getString(KEY_INITIAL_QUERY); -            boolean selectInitialQuery = msg.arg1 != 0; -            ComponentName launchActivity = -                    (ComponentName) msgData.getParcelable(KEY_LAUNCH_ACTIVITY); -            Bundle appSearchData = msgData.getBundle(KEY_APP_SEARCH_DATA); -            boolean globalSearch = msg.arg2 != 0; -            ISearchManagerCallback searchManagerCallback = (ISearchManagerCallback) msg.obj; -            int ident = msgData.getInt(KEY_IDENT); -            boolean trigger = msgData.getInt(KEY_TRIGGER) != 0; -            performStartSearch(initialQuery, selectInitialQuery, launchActivity, -                    appSearchData, globalSearch, searchManagerCallback, ident, trigger); -        } - -    } - -    /** -     * Actually launches the search UI. -     * This must be called on the search UI thread. -     */ -    void performStartSearch(String initialQuery, -            boolean selectInitialQuery, -            ComponentName launchActivity, -            Bundle appSearchData, -            boolean globalSearch, -            ISearchManagerCallback searchManagerCallback, -            int ident, -            boolean trigger) { -        if (DBG) debug("performStartSearch()"); - -        registerBroadcastReceiver(); -        mCallback = searchManagerCallback; - -        // clean up any hidden dialog that we were waiting to resume -        if (mStartedIdent != 0) { -            mSearchDialog.dismiss(); -        } - -        mStartedIdent = ident; -        if (DBG) Log.v(TAG, "******************* DIALOG: start"); - -        mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, -                globalSearch); -        mVisible = true; -        if (trigger) { -            mSearchDialog.launchQuerySearch(); -        } -    } - -    /** -     * Actually cancels the search UI. -     * This must be called on the search UI thread. -     */ -    void performStopSearch() { -        if (DBG) debug("performStopSearch()"); -        if (DBG) Log.v(TAG, "******************* DIALOG: cancel"); -        mSearchDialog.cancel(); -        mVisible = false; -        mStartedIdent = 0; -    } - -    /** -     * Updates the resumed activity -     * This must be called on the search UI thread. -     */ -    void performActivityResuming(int ident) { -        if (DBG) debug("performResumingActivity(): mStartedIdent=" -                + mStartedIdent + ", resuming: " + ident); -        this.mResumedIdent = ident; -        if (mStartedIdent != 0) { -            if (mStartedIdent == mResumedIdent) { -                // we are resuming into the activity where we previously hid the dialog, bring it -                // back -                if (DBG) Log.v(TAG, "******************* DIALOG: show"); -                mSearchDialog.show(); -                mVisible = true; -            } else { -                // resuming into some other activity; hide ourselves in case we ever come back -                // so we can show ourselves quickly again -                if (DBG) Log.v(TAG, "******************* DIALOG: hide"); -                mSearchDialog.hide(); -                mVisible = false; -            } -        } -    } - -    /** -     * Updates due to system dialogs being closed -     * This must be called on the search UI thread. -     */ -    void performClosingSystemDialogs(String reason) { -        if (DBG) debug("performClosingSystemDialogs(): mStartedIdent=" -                + mStartedIdent + ", reason: " + reason); -        if (!"search".equals(reason)) { -            if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); -            performStopSearch(); -        } -    } - -    /** -     * Must be called from the search UI thread. -     */ -    void performOnConfigurationChanged() { -        if (DBG) debug("performOnConfigurationChanged()"); -        mSearchDialog.onConfigurationChanged(); -    } - -    /** -     * Called by {@link SearchDialog} when it goes away. -     */ -    public void onDismiss(DialogInterface dialog) { -        if (DBG) debug("onDismiss()"); -        mStartedIdent = 0; -        mVisible = false; -        callOnDismiss(); - -        // we don't need the callback anymore, release it -        mCallback = null; -        unregisterBroadcastReceiver(); -    } - - -    /** -     * Called by {@link SearchDialog} when the user or activity cancels search. -     * Whenever this method is called, {@link #onDismiss} is always called afterwards. -     */ -    public void onCancel(DialogInterface dialog) { -        if (DBG) debug("onCancel()"); -        callOnCancel(); -    } - -    private void callOnDismiss() { -        if (mCallback == null) return; -        try { -            // should be safe to do on the search UI thread, since it's a oneway interface -            mCallback.onDismiss(); -        } catch (DeadObjectException ex) { -            // The process that hosted the callback has died, do nothing -        } catch (RemoteException ex) { -            Log.e(TAG, "onDismiss() failed: " + ex); -        } -    } - -    private void callOnCancel() { -        if (mCallback != null) { -            try { -                // should be safe to do on the search UI thread, since it's a oneway interface -                mCallback.onCancel(); -            } catch (DeadObjectException ex) { -                // The process that hosted the callback has died, do nothing -            } catch (RemoteException ex) { -                Log.e(TAG, "onCancel() failed: " + ex); -            } -        } -    } - -    private static void debug(String msg) { -        Thread thread = Thread.currentThread(); -        Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); -    } -} diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index f9a0b93c97ba..9953b5609535 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -51,10 +51,6 @@ public class SearchManagerService extends ISearchManager.Stub {      // Only accessed by ensureSearchablesCreated() and getSearchables()      private Searchables mSearchables; -    // This field is initialized in ensureSearchDialogCreated(), and then never modified. -    // Only accessed by ensureSearchDialogCreated() and getSearchDialog() -    private SearchDialogWrapper mSearchDialog; -      /**       * Initializes the Search Manager service in the provided system context.       * Only one instance of this object should be created! @@ -63,23 +59,6 @@ public class SearchManagerService extends ISearchManager.Stub {       */      public SearchManagerService(Context context)  {          mContext = context; -        // call initialize() after all pending actions on the main system thread have finished -        new Handler().post(new Runnable() { -            public void run() { -                initialize(); -            } -        }); -    } - -    /** -     * Initializes the list of searchable activities and the search UI. -     */ -    void initialize() { -        try { -            ActivityManagerNative.getDefault().registerActivityWatcher( -                    mActivityWatcher); -        } catch (RemoteException e) { -        }      }      private synchronized void ensureSearchablesCreated() { @@ -96,22 +75,11 @@ public class SearchManagerService extends ISearchManager.Stub {          mContext.registerReceiver(mPackageChangedReceiver, packageFilter);      } -    private synchronized void ensureSearchDialogCreated() { -        if (mSearchDialog != null) return; - -        mSearchDialog = new SearchDialogWrapper(mContext); -    } -      private synchronized Searchables getSearchables() {          ensureSearchablesCreated();          return mSearchables;      } -    private synchronized SearchDialogWrapper getSearchDialog() { -        ensureSearchDialogCreated(); -        return mSearchDialog; -    } -      /**       * Refreshes the "searchables" list when packages are added/removed.       */ @@ -124,8 +92,6 @@ public class SearchManagerService extends ISearchManager.Stub {                      Intent.ACTION_PACKAGE_REMOVED.equals(action) ||                      Intent.ACTION_PACKAGE_CHANGED.equals(action)) {                  if (DBG) Log.d(TAG, "Got " + action); -                // Dismiss search dialog, since the search context may no longer be valid -                getSearchDialog().stopSearch();                  // Update list of searchable activities                  getSearchables().buildSearchableList();                  broadcastSearchablesChanged(); @@ -133,19 +99,6 @@ public class SearchManagerService extends ISearchManager.Stub {          }      }; -    private IActivityWatcher.Stub mActivityWatcher = new IActivityWatcher.Stub() { -        public void activityResuming(int activityId) throws RemoteException { -            if (DBG) Log.i("foo", "********************** resuming: " + activityId); -            if (mSearchDialog == null) return; -            mSearchDialog.activityResuming(activityId); -        } -        public void closingSystemDialogs(String reason) { -            if (DBG) Log.i("foo", "********************** closing dialogs: " + reason); -            if (mSearchDialog == null) return; -            mSearchDialog.closingSystemDialogs(reason); -        } -    }; -          /**       * Informs all listeners that the list of searchables has been updated.       */ @@ -214,62 +167,4 @@ public class SearchManagerService extends ISearchManager.Stub {          getSearchables().setDefaultWebSearch(component);          broadcastSearchablesChanged();      } - -    // Search UI API - -    /** -     * Launches the search UI. Can be called from any thread. -     * -     * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) -     */ -    public void startSearch(String initialQuery, -            boolean selectInitialQuery, -            ComponentName launchActivity, -            Bundle appSearchData, -            boolean globalSearch, -            ISearchManagerCallback searchManagerCallback, -            int ident) { -        getSearchDialog().startSearch(initialQuery, -                selectInitialQuery, -                launchActivity, -                appSearchData, -                globalSearch, -                searchManagerCallback, -                ident, -                false); // don't trigger -    } - -    /** -     * Launches the search UI and triggers the search, as if the user had clicked on the -     * search button within the dialog. -     * -     * @see SearchManager#triggerSearch(String, android.content.ComponentName, android.os.Bundle) -     */ -    public void triggerSearch(String query, -            ComponentName launchActivity, -            Bundle appSearchData, -            ISearchManagerCallback searchManagerCallback, -            int ident) { -        getSearchDialog().startSearch( -                query, -                false, -                launchActivity, -                appSearchData, -                false, -                searchManagerCallback, -                ident, -                true); // triger search after launching -    } - -    /** -     * Cancels the search dialog. Can be called from any thread. -     */ -    public void stopSearch() { -        getSearchDialog().stopSearch(); -    } - -    public boolean isVisible() { -        return mSearchDialog != null && mSearchDialog.isVisible(); -    } -  } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 1337beda7038..3f1672af29bf 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -177,18 +177,21 @@ class BrowserFrame extends Handler {      /**       * Load a url from the network or the filesystem into the main frame. -     * Following the same behaviour as Safari, javascript: URLs are not -     * passed to the main frame, instead they are evaluated immediately. +     * Following the same behaviour as Safari, javascript: URLs are not passed +     * to the main frame, instead they are evaluated immediately.       * @param url The url to load. +     * @param extraHeaders The extra headers sent with this url. This should not +     *            include the common headers like "user-agent". If it does, it +     *            will be replaced by the intrinsic value of the WebView.       */ -    public void loadUrl(String url) { +    public void loadUrl(String url, Map<String, String> extraHeaders) {          mLoadInitFromJava = true;          if (URLUtil.isJavaScriptUrl(url)) {              // strip off the scheme and evaluate the string              stringByEvaluatingJavaScriptFromString(                      url.substring("javascript:".length()));          } else { -            nativeLoadUrl(url); +            nativeLoadUrl(url, extraHeaders);          }          mLoadInitFromJava = false;      } @@ -904,7 +907,7 @@ class BrowserFrame extends Handler {      /**       * Returns false if the url is bad.       */ -    private native void nativeLoadUrl(String url); +    private native void nativeLoadUrl(String url, Map<String, String> headers);      private native void nativePostUrl(String url, byte[] postData); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 93e72ffd41b2..ecea55fe4a9e 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1376,6 +1376,22 @@ public class WebView extends AbsoluteLayout      }      /** +     * Load the given url with the extra headers. +     * @param url The url of the resource to load. +     * @param extraHeaders The extra headers sent with this url. This should not +     *            include the common headers like "user-agent". If it does, it +     *            will be replaced by the intrinsic value of the WebView. +     */ +    public void loadUrl(String url, Map<String, String> extraHeaders) { +        switchOutDrawHistory(); +        WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData(); +        arg.mUrl = url; +        arg.mExtraHeaders = extraHeaders; +        mWebViewCore.sendMessage(EventHub.LOAD_URL, arg); +        clearTextEntry(); +    } + +    /**       * Load the given url.       * @param url The url of the resource to load.       */ @@ -1383,9 +1399,7 @@ public class WebView extends AbsoluteLayout          if (url == null) {              return;          } -        switchOutDrawHistory(); -        mWebViewCore.sendMessage(EventHub.LOAD_URL, url); -        clearTextEntry(); +        loadUrl(url, null);      }      /** diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 6700d712cfb4..1e21cb4ccaab 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -686,6 +686,11 @@ final class WebViewCore {          int mY;      } +    static class GetUrlData { +        String mUrl; +        Map<String, String> mExtraHeaders; +    } +      static class PostUrlData {          String mUrl;          byte[] mPostData; @@ -958,9 +963,11 @@ final class WebViewCore {                                      ((Float) msg.obj).floatValue(), msg.arg1);                              break; -                        case LOAD_URL: -                            loadUrl((String) msg.obj); +                        case LOAD_URL: { +                            GetUrlData param = (GetUrlData) msg.obj; +                            loadUrl(param.mUrl, param.mExtraHeaders);                              break; +                        }                          case POST_URL: {                              PostUrlData param = (PostUrlData) msg.obj; @@ -1545,9 +1552,9 @@ final class WebViewCore {          }      } -    private void loadUrl(String url) { +    private void loadUrl(String url, Map<String, String> extraHeaders) {          if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); -        mBrowserFrame.loadUrl(url); +        mBrowserFrame.loadUrl(url, extraHeaders);      }      private void key(KeyEvent evt, boolean isDown) { diff --git a/core/java/android/os/MailboxNotAvailableException.java b/core/java/com/android/internal/app/IMediaContainerService.aidl index 574adbd196b1..726e28f51551 100644..100755 --- a/core/java/android/os/MailboxNotAvailableException.java +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -1,5 +1,5 @@  /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2008 The Android Open Source Project   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -14,24 +14,15 @@   * limitations under the License.   */ -package android.os; +package com.android.internal.app; -/** @hide */ -public class MailboxNotAvailableException extends Throwable -{ -  /** -   * This exception represents the case when a request for a -   * named, published mailbox fails because the requested name has not been published -   */ +import android.net.Uri; +import android.os.ParcelFileDescriptor; -    public -    MailboxNotAvailableException() -    { -    } - -    public -    MailboxNotAvailableException(String s) -    { -        super(s); -    } -} +interface IMediaContainerService { +    String copyResourceToContainer(in Uri packageURI, +                String containerId, +                String key, String resFileName); +    boolean copyResource(in Uri packageURI, +                in ParcelFileDescriptor outStream); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/util/HanziToPinyin.java b/core/java/com/android/internal/util/HanziToPinyin.java index 4368e9842254..6a4adaa0f6e1 100644 --- a/core/java/com/android/internal/util/HanziToPinyin.java +++ b/core/java/com/android/internal/util/HanziToPinyin.java @@ -16,8 +16,6 @@  package com.android.internal.util; -import com.google.android.util.AbstractMessageParser.Token; -  import android.text.TextUtils;  import android.util.Log; @@ -298,8 +296,10 @@ public class HanziToPinyin {          };      /** First and last Chinese character with known Pinyin according to zh collation */ -    private static final String FIRST_UNIHAN =  "\u5416"; -    private static final String LAST_UNIHAN =  "\u5497"; +    private static final String FIRST_PINYIN_UNIHAN =  "\u5416"; +    private static final String LAST_PINYIN_UNIHAN =  "\u5497"; +    /** The first Chinese character in Unicode block */ +    private static final char FIRST_UNIHAN = '\u3400';      private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);      private static HanziToPinyin sInstance; @@ -311,10 +311,18 @@ public class HanziToPinyin {           */          public static final String SEPARATOR = " "; -        public static final int ASCII = 1; +        public static final int LATIN = 1;          public static final int PINYIN = 2;          public static final int UNKNOWN = 3; +        public Token() { +        } + +        public Token(int type, String source, String target) { +            this.type = type; +            this.source = source; +            this.target = target; +        }          /**           * Type of this token, ASCII, PINYIN or UNKNOWN.           */ @@ -347,6 +355,7 @@ public class HanziToPinyin {                      return sInstance;                  }              } +            Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled");              sInstance = new HanziToPinyin(false);              return sInstance;          } @@ -359,11 +368,15 @@ public class HanziToPinyin {          int offset = -1;          int cmp;          if (character < 256) { -            token.type = Token.ASCII; +            token.type = Token.LATIN; +            token.target = letter; +            return token; +        } else if (character < FIRST_UNIHAN) { +            token.type = Token.UNKNOWN;              token.target = letter;              return token;          } else { -            cmp = COLLATOR.compare(letter, FIRST_UNIHAN); +            cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN);              if (cmp < 0) {                  token.type = Token.UNKNOWN;                  token.target = letter; @@ -372,7 +385,7 @@ public class HanziToPinyin {                  token.type = Token.PINYIN;                  offset = 0;              } else { -                cmp = COLLATOR.compare(letter, LAST_UNIHAN); +                cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN);                  if (cmp > 0) {                      token.type = Token.UNKNOWN;                      token.target = letter; @@ -412,44 +425,71 @@ public class HanziToPinyin {          return token;      } +    /** +     * Convert the input to a array of tokens. The sequence of ASCII or Unknown +     * characters without space will be put into a Token, One Hanzi character  +     * which has pinyin will be treated as a Token. +     * If these is no China collator, the empty token array is returned. +     */      public ArrayList<Token> get(final String input) { +        ArrayList<Token> tokens = new ArrayList<Token>();          if (!mHasChinaCollator || TextUtils.isEmpty(input)) { -            return null; +            // return empty tokens. +            return tokens;          } - -        ArrayList<Token> tokens = new ArrayList<Token>(); -        Token currentToken; -          final int inputLength = input.length(); - -        currentToken = getToken(input.charAt(0)); - -        for (int i = 1; i < inputLength; i++) { +        final StringBuilder sb = new StringBuilder(); +        int tokenType = Token.LATIN; +        // Go through the input, create a new token when +        // a. Token type changed +        // b. Get the Pinyin of current charater. +        // c. current character is space. +        for (int i = 0; i < inputLength; i++) {              final char character = input.charAt(i); -            Token token = getToken(character); - -            if (token.type != currentToken.type) { -                currentToken.target = currentToken.target.trim(); -                tokens.add(currentToken); -                currentToken = token; +            if (character == ' ') { +                if (sb.length() > 0) { +                    addToken(sb, tokens, tokenType); +                } +            } else if (character < 256) { +                if (tokenType != Token.LATIN && sb.length() > 0) { +                    addToken(sb, tokens, tokenType); +                } +                tokenType = Token.LATIN; +                sb.append(character); +            } else if (character < FIRST_UNIHAN) { +                if (tokenType != Token.UNKNOWN && sb.length() > 0) { +                    addToken(sb, tokens, tokenType); +                } +                tokenType = Token.UNKNOWN; +                sb.append(character);              } else { -                switch (token.type) { -                    case Token.ASCII: -                    case Token.UNKNOWN: -                        currentToken.source += token.source; -                        currentToken.target += token.target; -                        break; -                    case Token.PINYIN: -                        currentToken.source += token.source; -                        currentToken.target += " " + token.target; -                        break; +                Token t = getToken(character); +                if (t.type == Token.PINYIN) { +                    if (sb.length() > 0) { +                        addToken(sb, tokens, tokenType); +                    } +                    tokens.add(t); +                    tokenType = Token.PINYIN; +                } else { +                    if (tokenType != t.type && sb.length() > 0) { +                        addToken(sb, tokens, tokenType); +                    } +                    tokenType = t.type; +                    sb.append(character);                  }              }          } - -        currentToken.target = currentToken.target.trim(); -        tokens.add(currentToken); - +        if (sb.length() > 0) { +            addToken(sb, tokens, tokenType); +        }          return tokens;      } + +    private void addToken(final StringBuilder sb, final ArrayList<Token> tokens, +            final int tokenType) { +        String str = sb.toString(); +        tokens.add(new Token(tokenType, str, str)); +        sb.setLength(0); +    } +  } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 67a0bdab207b..7fd58e89707d 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -99,6 +99,7 @@ LOCAL_SRC_FILES:= \  	android/graphics/Shader.cpp \  	android/graphics/Typeface.cpp \  	android/graphics/Xfermode.cpp \ +	android/graphics/YuvToJpegEncoder.cpp \  	android_media_AudioRecord.cpp \  	android_media_AudioSystem.cpp \  	android_media_AudioTrack.cpp \ @@ -148,6 +149,7 @@ LOCAL_C_INCLUDES += \  	external/tremor/Tremor \  	external/icu4c/i18n \  	external/icu4c/common \ +	external/jpeg \  	frameworks/opt/emoji  LOCAL_SHARED_LIBRARIES := \ @@ -175,7 +177,8 @@ LOCAL_SHARED_LIBRARIES := \  	libicui18n \  	libicudata \  	libmedia \ -	libwpa_client +	libwpa_client \ +	libjpeg  ifeq ($(BOARD_HAVE_BLUETOOTH),true)  LOCAL_C_INCLUDES += \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 8364838a153e..fa1ee0da75be 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -64,6 +64,7 @@ extern int register_android_graphics_PathEffect(JNIEnv* env);  extern int register_android_graphics_Region(JNIEnv* env);  extern int register_android_graphics_Shader(JNIEnv* env);  extern int register_android_graphics_Typeface(JNIEnv* env); +extern int register_android_graphics_YuvImage(JNIEnv* env);  extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);  extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env); @@ -1215,6 +1216,7 @@ static const RegJNIRec gRegJNI[] = {      REG_JNI(register_android_graphics_Shader),      REG_JNI(register_android_graphics_Typeface),      REG_JNI(register_android_graphics_Xfermode), +    REG_JNI(register_android_graphics_YuvImage),      REG_JNI(register_com_android_internal_graphics_NativeUtils),      REG_JNI(register_android_database_CursorWindow), diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp new file mode 100644 index 000000000000..ef5c9aeb6142 --- /dev/null +++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp @@ -0,0 +1,252 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "SkJpegUtility.h" +#include "YuvToJpegEncoder.h" +#include "ui/PixelFormat.h" + +#include <jni.h> + +YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { +    // Only PIXEL_FORMAT_YCbCr_420_SP and PIXEl_FOMAT_YCbCr_422_I are supported +    // for now. +    if (format == android::PIXEL_FORMAT_YCbCr_420_SP) { +        return new Yuv420SpToJpegEncoder(strides); +    } else if (format == android::PIXEL_FORMAT_YCbCr_422_I) { +        return new Yuv422IToJpegEncoder(strides); +    } else { +      return NULL; +    } +} + +YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { +} + +bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, +        int height, int* offsets, int jpegQuality) { +    jpeg_compress_struct    cinfo; +    skjpeg_error_mgr        sk_err; +    skjpeg_destination_mgr  sk_wstream(stream); + +    cinfo.err = jpeg_std_error(&sk_err); +    sk_err.error_exit = skjpeg_error_exit; +    if (setjmp(sk_err.fJmpBuf)) { +        return false; +    } +    jpeg_create_compress(&cinfo); + +    cinfo.dest = &sk_wstream; + +    setJpegCompressStruct(&cinfo, width, height, jpegQuality); + +    jpeg_start_compress(&cinfo, TRUE); + +    compress(&cinfo, (uint8_t*) inYuv, offsets); + +    jpeg_finish_compress(&cinfo); + +    return true; +} + +void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, +        int width, int height, int quality) { +    jpeg_set_quality(cinfo, quality, TRUE); + +    cinfo->image_width = width; +    cinfo->image_height = height; + +    cinfo->input_components = 3; +    cinfo->in_color_space = JCS_YCbCr; +    jpeg_set_defaults(cinfo); +    jpeg_set_colorspace(cinfo, JCS_YCbCr); +    cinfo->raw_data_in = TRUE; + +    cinfo->dct_method = JDCT_IFAST; + +    configSamplingFactors(cinfo); +} + +/////////////////////////////////////////////////////////////////// +Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) : +        YuvToJpegEncoder(strides) { +    fNumPlanes = 2; +} + +void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, +        uint8_t* yuv, int* offsets) { +    SkDebugf("onFlyCompress"); +    JSAMPROW y[16]; +    JSAMPROW cb[8]; +    JSAMPROW cr[8]; +    JSAMPARRAY planes[3]; +    planes[0] = y; +    planes[1] = cb; +    planes[2] = cr; + +    int width = cinfo->image_width; +    int height = cinfo->image_height; +    uint8_t* yPlanar = yuv + offsets[0]; +    uint8_t* vuPlanar = yuv + offsets[1]; //width * height; +    uint8_t* uRows = new uint8_t [8 * (width >> 1)]; +    uint8_t* vRows = new uint8_t [8 * (width >> 1)]; + + +    // process 16 lines of Y and 8 lines of U/V each time. +    while (cinfo->next_scanline < cinfo->image_height) { +        //deitnerleave u and v +        deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width); + +        for (int i = 0; i < 16; i++) { +            // y row +            y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; + +            // construct u row and v row +            if ((i & 1) == 0) { +                // height and width are both halved because of downsampling +                int offset = (i >> 1) * (width >> 1); +                cb[i/2] = uRows + offset; +                cr[i/2] = vRows + offset; +            } +          } +        jpeg_write_raw_data(cinfo, planes, 16); +    } +    delete [] uRows; +    delete [] vRows; + +} + +void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, +        uint8_t* vRows, int rowIndex, int width) { +    for (int row = 0; row < 8; ++row) { +        int offset = ((rowIndex >> 1) + row) * fStrides[1]; +        uint8_t* vu = vuPlanar + offset; +        for (int i = 0; i < (width >> 1); ++i) { +            int index = row * (width >> 1) + i; +            uRows[index] = vu[1]; +            vRows[index] = vu[0]; +            vu += 2; +        } +    } +} + +void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { +    // cb and cr are horizontally downsampled and vertically downsampled as well. +    cinfo->comp_info[0].h_samp_factor = 2; +    cinfo->comp_info[0].v_samp_factor = 2; +    cinfo->comp_info[1].h_samp_factor = 1; +    cinfo->comp_info[1].v_samp_factor = 1; +    cinfo->comp_info[2].h_samp_factor = 1; +    cinfo->comp_info[2].v_samp_factor = 1; +} + +/////////////////////////////////////////////////////////////////////////////// +Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) : +        YuvToJpegEncoder(strides) { +    fNumPlanes = 1; +} + +void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo, +        uint8_t* yuv, int* offsets) { +    SkDebugf("onFlyCompress_422"); +    JSAMPROW y[16]; +    JSAMPROW cb[16]; +    JSAMPROW cr[16]; +    JSAMPARRAY planes[3]; +    planes[0] = y; +    planes[1] = cb; +    planes[2] = cr; + +    int width = cinfo->image_width; +    int height = cinfo->image_height; +    uint8_t* yRows = new uint8_t [16 * width]; +    uint8_t* uRows = new uint8_t [16 * (width >> 1)]; +    uint8_t* vRows = new uint8_t [16 * (width >> 1)]; + +    uint8_t* yuvOffset = yuv + offsets[0]; + +    // process 16 lines of Y and 16 lines of U/V each time. +    while (cinfo->next_scanline < cinfo->image_height) { +        deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height); + +        for (int i = 0; i < 16; i++) { +            // y row +            y[i] = yRows + i * width; + +            // construct u row and v row +            // width is halved because of downsampling +            int offset = i * (width >> 1); +            cb[i] = uRows + offset; +            cr[i] = vRows + offset; +        } + +        jpeg_write_raw_data(cinfo, planes, 16); +    } +    delete [] yRows; +    delete [] uRows; +    delete [] vRows; +} + + +void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, +        uint8_t* vRows, int rowIndex, int width, int height) { +    for (int row = 0; row < 16; ++row) { +        uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0]; +        for (int i = 0; i < (width >> 1); ++i) { +            int indexY = row * width + (i << 1); +            int indexU = row * (width >> 1) + i; +            yRows[indexY] = yuvSeg[0]; +            yRows[indexY + 1] = yuvSeg[2]; +            uRows[indexU] = yuvSeg[1]; +            vRows[indexU] = yuvSeg[3]; +            yuvSeg += 4; +        } +    } +} + +void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { +    // cb and cr are horizontally downsampled and vertically downsampled as well. +    cinfo->comp_info[0].h_samp_factor = 2; +    cinfo->comp_info[0].v_samp_factor = 2; +    cinfo->comp_info[1].h_samp_factor = 1; +    cinfo->comp_info[1].v_samp_factor = 2; +    cinfo->comp_info[2].h_samp_factor = 1; +    cinfo->comp_info[2].v_samp_factor = 2; +} +/////////////////////////////////////////////////////////////////////////////// + +static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, +        int format, int width, int height, jintArray offsets, +        jintArray strides, int jpegQuality, jobject jstream, +        jbyteArray jstorage) { +    jbyte* yuv = env->GetByteArrayElements(inYuv, NULL); +    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + +    jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); +    jint* imgStrides = env->GetIntArrayElements(strides, NULL); +    YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); +    if (encoder == NULL) { +        return false; +    } +    encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); + +    delete encoder; +    env->ReleaseByteArrayElements(inYuv, yuv, 0); +    env->ReleaseIntArrayElements(offsets, imgOffsets, 0); +    env->ReleaseIntArrayElements(strides, imgStrides, 0); +    return true; +} +/////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gYuvImageMethods[] = { +    {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z", +        (void*)YuvImage_compressToJpeg } +}; + +#define kClassPathName  "android/graphics/YuvImage" + +int register_android_graphics_YuvImage(JNIEnv* env); +int register_android_graphics_YuvImage(JNIEnv* env) +{ +    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, +            gYuvImageMethods, SK_ARRAY_COUNT(gYuvImageMethods)); +} diff --git a/core/jni/android/graphics/YuvToJpegEncoder.h b/core/jni/android/graphics/YuvToJpegEncoder.h new file mode 100644 index 000000000000..97106ce62845 --- /dev/null +++ b/core/jni/android/graphics/YuvToJpegEncoder.h @@ -0,0 +1,74 @@ +#ifndef YuvToJpegEncoder_DEFINED +#define YuvToJpegEncoder_DEFINED + +#include "SkTypes.h" +#include "SkStream.h" +extern "C" { +    #include "jpeglib.h" +    #include "jerror.h" +} + +class YuvToJpegEncoder { +public: +    /** Create an encoder based on the YUV format. +     * +     *  @param pixelFormat The yuv pixel format as defined in ui/PixelFormat.h. +     *  @param strides The number of row bytes in each image plane. +     *  @return an encoder based on the pixelFormat. +     */ +    static YuvToJpegEncoder* create(int pixelFormat, int* strides); + +    YuvToJpegEncoder(int* strides); + +    /** Encode YUV data to jpeg,  which is output to a stream. +     * +     *  @param stream The jpeg output stream. +     *  @param inYuv The input yuv data. +     *  @param width Width of the the Yuv data in terms of pixels. +     *  @param height Height of the Yuv data in terms of pixels. +     *  @param offsets The offsets in each image plane with respect to inYuv. +     *  @param jpegQuality Picture quality in [0, 100]. +     *  @return true if successfully compressed the stream. +     */ +    bool encode(SkWStream* stream,  void* inYuv, int width, +           int height, int* offsets, int jpegQuality); + +    virtual ~YuvToJpegEncoder() {} + +protected: +    int fNumPlanes; +    int* fStrides; +    void setJpegCompressStruct(jpeg_compress_struct* cinfo, int width, +            int height, int quality); +    virtual void configSamplingFactors(jpeg_compress_struct* cinfo) = 0; +    virtual void compress(jpeg_compress_struct* cinfo, +            uint8_t* yuv, int* offsets) = 0; +}; + +class Yuv420SpToJpegEncoder : public YuvToJpegEncoder { +public: +     Yuv420SpToJpegEncoder(int* strides); +     virtual ~Yuv420SpToJpegEncoder() {} + +private: +     void configSamplingFactors(jpeg_compress_struct* cinfo); +     void deinterleaveYuv(uint8_t* yuv, int width, int height, +            uint8_t*& yPlanar, uint8_t*& uPlanar, uint8_t*& vPlanar); +     void deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows, +             int rowIndex, int width); +     void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets); +}; + +class Yuv422IToJpegEncoder : public YuvToJpegEncoder { +public: +    Yuv422IToJpegEncoder(int* strides); +    virtual ~Yuv422IToJpegEncoder() {} + +private: +    void configSamplingFactors(jpeg_compress_struct* cinfo); +    void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets); +    void deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, +            uint8_t* vRows, int rowIndex, int width, int height); +}; + +#endif diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 64ee4f0c9881..1a5987cc3c65 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -546,6 +546,18 @@ static void android_hardware_Camera_stopSmoothZoom(JNIEnv *env, jobject thiz)      }  } +static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject thiz, +        jint value) +{ +    LOGV("setDisplayOrientation"); +    sp<Camera> camera = get_native_camera(env, thiz, NULL); +    if (camera == 0) return; + +    if (camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION, value, 0) != NO_ERROR) { +        jniThrowException(env, "java/lang/RuntimeException", "set display orientation failed"); +    } +} +  //-------------------------------------------------  static JNINativeMethod camMethods[] = { @@ -603,6 +615,9 @@ static JNINativeMethod camMethods[] = {    { "stopSmoothZoom",      "()V",      (void *)android_hardware_Camera_stopSmoothZoom }, +  { "setDisplayOrientation", +    "(I)V", +    (void *)android_hardware_Camera_setDisplayOrientation },  };  struct field { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d81476aac046..54e15a598d54 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -700,6 +700,46 @@          android:label="@string/permlab_mount_format_filesystems"          android:description="@string/permdesc_mount_format_filesystems" /> +    <!-- Allows access to ASEC non-destructive API calls +         @hide  --> +    <permission android:name="android.permission.ASEC_ACCESS" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="dangerous" +        android:label="@string/permlab_asec_access" +        android:description="@string/permdesc_asec_access" /> + +    <!-- Allows creation of ASEC volumes +         @hide  --> +    <permission android:name="android.permission.ASEC_CREATE" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="dangerous" +        android:label="@string/permlab_asec_create" +        android:description="@string/permdesc_asec_create" /> + +    <!-- Allows destruction of ASEC volumes +         @hide  --> +    <permission android:name="android.permission.ASEC_DESTROY" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="dangerous" +        android:label="@string/permlab_asec_destroy" +        android:description="@string/permdesc_asec_destroy" /> + +    <!-- Allows mount / unmount of ASEC volumes +         @hide  --> +    <permission android:name="android.permission.ASEC_MOUNT_UNMOUNT" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="dangerous" +        android:label="@string/permlab_asec_mount_unmount" +        android:description="@string/permdesc_asec_mount_unmount" /> + +    <!-- Allows rename of ASEC volumes +         @hide  --> +    <permission android:name="android.permission.ASEC_RENAME" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="dangerous" +        android:label="@string/permlab_asec_rename" +        android:description="@string/permdesc_asec_rename" /> +      <!-- Allows applications to disable the keyguard -->      <permission android:name="android.permission.DISABLE_KEYGUARD"          android:permissionGroup="android.permission-group.SYSTEM_TOOLS" @@ -1162,6 +1202,16 @@          android:description="@string/permdesc_cache_filesystem"          android:protectionLevel="signatureOrSystem" /> +    <!-- Must be required by default container service so that only +         the system can bind to it and use it to copy +         protected data to secure containers or files +         accessible to the system. +         @hide --> +    <permission android:name="android.permission.COPY_PROTECTED_DATA" +        android:label="@string/permlab_copyProtectedData" +        android:description="@string/permlab_copyProtectedData" +        android:protectionLevel="signature" /> +      <application android:process="system"                   android:persistent="true"                   android:hasCode="false" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0ef07ff58552..259398f0e629 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -877,6 +877,31 @@      <string name="permdesc_mount_format_filesystems">Allows the application to format removable storage.</string>      <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permlab_asec_access">get information on secure storage</string> +    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permdesc_asec_access">Allows the application to get information on secure storage.</string> + +    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permlab_asec_create">create secure storage</string> +    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permdesc_asec_create">Allows the application to create secure storage.</string> + +    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permlab_asec_destroy">destroy secure storage</string> +    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permdesc_asec_destroy">Allows the application to destroy secure storage.</string> + +    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permlab_asec_mount_unmount">mount / unmount secure storage</string> +    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permdesc_asec_mount_unmount">Allows the application to mount / unmount secure storage.</string> + +    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permlab_asec_rename">rename secure storage</string> +    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> +    <string name="permdesc_asec_rename">Allows the application to rename secure storage.</string> + +    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->      <string name="permlab_vibrate">control vibrator</string>      <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->      <string name="permdesc_vibrate">Allows the application to control @@ -1167,10 +1192,10 @@      <!-- Title of policy access to watch user login attempts -->      <string name="policylab_watchLogin">Watch login attempts</string>      <!-- Description of policy access to watch user login attempts --> -    <string name="policydesc_watchLogin">Monitor attempts to login to -        the device, in particular to respond to failed login attempts.</string> +    <string name="policydesc_watchLogin">Monitor failed attempts to login to +        the device, to perform some action.</string>      <!-- Title of policy access to reset user's password --> -    <string name="policylab_resetPassword">Reset your password</string> +    <string name="policylab_resetPassword">Reset password</string>      <!-- Description of policy access to reset user's password -->      <string name="policydesc_resetPassword">Force your password          to a new value, requiring the administrator give it to you @@ -1184,12 +1209,12 @@      <string name="policylab_forceLock">Force lock</string>      <!-- Description of policy access to limiting the user's password choices -->      <string name="policydesc_forceLock">Force the device to immediately lock, -        requiring that its password is re-entered.</string> +        requiring that you re-enter its password.</string>      <!-- Title of policy access to wipe the user's data -->      <string name="policylab_wipeData">Erase all data</string>      <!-- Description of policy access to wipe the user's data -->      <string name="policydesc_wipeData">Perform a factory reset, deleting -        all of your data without any confirmation from you.</string> +        all of your data without any confirmation.</string>      <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />      <!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. --> @@ -2077,6 +2102,12 @@      <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->      <string name="permdesc_pkgUsageStats">Allows the modification of collected component usage statistics. Not for use by normal applications.</string> +    <!-- permission attributes related to default container service --> +    <!-- Title of an application permission that lets an application use default container service. --> +    <string name="permlab_copyProtectedData">Allows to invoke default container service to copy content. Not for use by normal applications.</string> +    <!-- Description of an application permission,  used to invoke default container service to copy content. --> +    <string name="permdesc_copyProtectedData">Allows to invoke default container service to copy content. Not for use by normal applications.</string> +      <!-- Shown in the tutorial for tap twice for zoom control. -->      <string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk new file mode 100644 index 000000000000..77c176f622cf --- /dev/null +++ b/core/tests/coretests/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_PACKAGE_NAME := FrameworksCoreTests + +include $(BUILD_PACKAGE) + diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml new file mode 100644 index 000000000000..674bda795275 --- /dev/null +++ b/core/tests/coretests/AndroidManifest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at +   +          http://www.apache.org/licenses/LICENSE-2.0 +   +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" +          package="com.android.frameworks.coretests"> +    <uses-permission android:name="android.permission.RECEIVE_SMS"/> +    <uses-permission android:name="android.permission.INTERNET" /> +    <uses-permission android:name="android.permission.READ_CONTACTS" /> +    <uses-permission android:name="android.permission.WRITE_CONTACTS" /> +    <uses-permission android:name="android.permission.WAKE_LOCK" /> +    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> +    <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" /> +    <uses-permission android:name="android.permission.BROADCAST_STICKY" /> + +    <!-- location test permissions --> +    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> +    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> +    <uses-permission android:name="android.permission.WRITE_SETTINGS"/> +    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> +    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> +     +    <application> +        <uses-library android:name="android.test.runner" /> +        <activity android:name="StubTestBrowserActivity" android:label="Stubbed Test Browser"> +            <intent-filter> +                <action android:name="android.intent.action.MAIN"/> +                <category android:name="android.intent.category.FOR_TESTS_ONLY"/> +            </intent-filter> +        </activity> + +        <activity android:name="android.test.TestBrowserTests" android:label="Test Browser Tests"> +            <intent-filter> +                <action android:name="android.intent.action.MAIN"/> +                <category android:name="android.intent.category.UNIT_TEST"/> +            </intent-filter> +        </activity> +    </application> + +    <instrumentation +    	android:name="android.test.InstrumentationTestRunner" +    	android:targetPackage="com.android.frameworks.coretests" +    	android:label="Frameworks Core Tests" /> +</manifest> diff --git a/tests/CoreTests/android/core/AtParserTest.java b/core/tests/coretests/src/android/bluetooth/AtParserTest.java index 09cb6e97d24e..c5aa52b07059 100644 --- a/tests/CoreTests/android/core/AtParserTest.java +++ b/core/tests/coretests/src/android/bluetooth/AtParserTest.java @@ -14,7 +14,7 @@   * limitations under the License.   */ -package android.core; +package android.bluetooth;  import android.bluetooth.AtCommandHandler;  import android.bluetooth.AtCommandResult; diff --git a/tests/CoreTests/android/content/ObserverNodeTest.java b/core/tests/coretests/src/android/content/ObserverNodeTest.java index 68cc75b7b2ad..68cc75b7b2ad 100644 --- a/tests/CoreTests/android/content/ObserverNodeTest.java +++ b/core/tests/coretests/src/android/content/ObserverNodeTest.java diff --git a/tests/CoreTests/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java index 533338e027e2..533338e027e2 100644 --- a/tests/CoreTests/android/content/SyncStorageEngineTest.java +++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java diff --git a/tests/CoreTests/android/database/MatrixCursorTest.java b/core/tests/coretests/src/android/database/MatrixCursorTest.java index cddc6c41ed2c..cddc6c41ed2c 100644 --- a/tests/CoreTests/android/database/MatrixCursorTest.java +++ b/core/tests/coretests/src/android/database/MatrixCursorTest.java diff --git a/tests/CoreTests/android/core/AbstractJDBCDriverTest.java b/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java index e381a5ef557a..e381a5ef557a 100644 --- a/tests/CoreTests/android/core/AbstractJDBCDriverTest.java +++ b/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java diff --git a/tests/CoreTests/android/core/SQLiteJDBCDriverTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java index eec82aa7e371..eec82aa7e371 100644 --- a/tests/CoreTests/android/core/SQLiteJDBCDriverTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java diff --git a/tests/CoreTests/android/core/RecurrenceSetTest.java b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java index cee324c5235d..64cd6c426dce 100644 --- a/tests/CoreTests/android/core/RecurrenceSetTest.java +++ b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java @@ -14,7 +14,7 @@   * limitations under the License.   */ -package android.core; +package android.pim;  import android.content.ContentValues;  import android.pim.ICalendar; diff --git a/tests/CoreTests/android/util/DayOfMonthCursorTest.java b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java index 4c5ad76d8193..4c5ad76d8193 100644 --- a/tests/CoreTests/android/util/DayOfMonthCursorTest.java +++ b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java diff --git a/tests/CoreTests/android/util/FloatMathTest.java b/core/tests/coretests/src/android/util/FloatMathTest.java index f479e2b45f30..f479e2b45f30 100644 --- a/tests/CoreTests/android/util/FloatMathTest.java +++ b/core/tests/coretests/src/android/util/FloatMathTest.java diff --git a/tests/CoreTests/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java index 5207ad918943..5207ad918943 100644 --- a/tests/CoreTests/android/util/MonthDisplayHelperTest.java +++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java diff --git a/tests/CoreTests/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java index e481ce04c82f..e481ce04c82f 100644 --- a/tests/CoreTests/android/util/StateSetTest.java +++ b/core/tests/coretests/src/android/util/StateSetTest.java diff --git a/tests/CoreTests/android/view/FocusFinderTest.java b/core/tests/coretests/src/android/view/FocusFinderTest.java index 7ac8dfce6e91..7ac8dfce6e91 100644 --- a/tests/CoreTests/android/view/FocusFinderTest.java +++ b/core/tests/coretests/src/android/view/FocusFinderTest.java diff --git a/tests/CoreTests/android/view/ViewGroupAttributesTest.java b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java index b4ef0e7e76bd..b4ef0e7e76bd 100644 --- a/tests/CoreTests/android/view/ViewGroupAttributesTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java diff --git a/tests/CoreTests/android/webkit/UrlInterceptRegistryTest.java b/core/tests/coretests/src/android/webkit/UrlInterceptRegistryTest.java index 750444993993..750444993993 100644 --- a/tests/CoreTests/android/webkit/UrlInterceptRegistryTest.java +++ b/core/tests/coretests/src/android/webkit/UrlInterceptRegistryTest.java diff --git a/tests/CoreTests/com/android/internal/util/PredicatesTest.java b/core/tests/coretests/src/com/android/internal/util/PredicatesTest.java index c46ff051dd33..c46ff051dd33 100644 --- a/tests/CoreTests/com/android/internal/util/PredicatesTest.java +++ b/core/tests/coretests/src/com/android/internal/util/PredicatesTest.java diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java new file mode 100644 index 000000000000..4a3bd47bc97d --- /dev/null +++ b/graphics/java/android/graphics/YuvImage.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import java.io.OutputStream; + +/** + * @hide pending API council approval + * + * YuvImage contains YUV data and provides a method that compresses a region of + * the YUV data to a Jpeg. The YUV data should be provided as a single byte + * array irrespective of the number of image planes in it. The stride of each + * image plane should be provided as well. + * + * To compress a rectangle region in the YUV data, users have to specify a + * region by width, height and offsets, where each image plane has a + * corresponding offset. All offsets are measured as a displacement in bytes + * from yuv[0], where yuv[0] is the beginning of the yuv data. + */ +public class YuvImage { + +    /** +     * Number of bytes of temp storage we use for communicating between the +     * native compressor and the java OutputStream. +     */ +    private final static int WORKING_COMPRESS_STORAGE = 4096; + +   /** +     * The YUV format as defined in {@link PixelFormat}. +     */ +    private int mFormat; + +    /** +     * The raw YUV data. +     * In the case of more than one image plane, the image planes must be +     * concatenated into a single byte array. +     */ +    private byte[] mData; + +    /** +     * The number of row bytes in each image plane. +     */ +    private int[] mStrides; + +    /** +     * Construct an YuvImage. +     * +     * @param yuv The YUV data. In the case of more than one image plane, all the planes must be +     *            concatenated into a single byte array. +     * @param format The YUV data format as defined in {@link PixelFormat}. +     * @param strides Row bytes of each image plane. +     */ +    public YuvImage(byte[] yuv, int format, int[] strides) { +        if ((yuv == null) || (strides == null)) { +            throw new IllegalArgumentException( +                    "yuv or strides cannot be null"); +        } +        mData = yuv; +        mFormat = format; +        mStrides = strides; +    } + +    /** +     * Compress a rectangle region in the YuvImage to a jpeg. +     * Only PixelFormat.YCbCr_420_SP and PixelFormat.YCbCr_422_I +     * are supported for now. +     * +     * @param width The width of the rectangle region. +     * @param height The height of the rectangle region. +     * @param offsets The offsets of the rectangle region in each image plane. +     *                The offsets are measured as a displacement in bytes from +     *                yuv[0], where yuv[0] is the beginning of the yuv data. +     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for +     *                 small size, 100 meaning compress for max quality. +     * @param stream   The outputstream to write the compressed data. +     * +     * @return true if successfully compressed to the specified stream. +     * +     */ +    public boolean compressToJpeg(int width, int height, int[] offsets, int quality, +            OutputStream stream) { +        if (!validate(mFormat, width, height, offsets)) { +            return false; +        } + +        if (quality < 0 || quality > 100) { +            throw new IllegalArgumentException("quality must be 0..100"); +        } + +        if (stream == null) { +            throw new NullPointerException(); +        } + +        return nativeCompressToJpeg(mData, mFormat, width, height, offsets, +                mStrides, quality, stream, new byte[WORKING_COMPRESS_STORAGE]); +    } + +    /** +     * @return the YUV data. +     */ +    public byte[] getYuvData() { +        return mData; +    } + +    /** +     * @return the YUV format as defined in {@link PixelFormat}. +     */ +    public int getYuvFormat() { +        return mFormat; +    } + +    /** +     * @return the number of row bytes in each image plane. +     */ +    public int[] getStrides() { +        return mStrides; +    } + +    protected boolean validate(int format, int width, int height, int[] offsets) { +        if (format != PixelFormat.YCbCr_420_SP && +                format != PixelFormat.YCbCr_422_I) { +            throw new IllegalArgumentException( +                    "only support PixelFormat.YCbCr_420_SP " + +                    "and PixelFormat.YCbCr_422_I for now"); +        } + +        if (offsets.length != mStrides.length) { +            throw new IllegalArgumentException( +                    "the number of image planes are mismatched"); +        } + +        if (width <= 0  || height <= 0) { +            throw new IllegalArgumentException( +                    "width and height must large than 0"); +        } + +        int requiredSize; +        if (format == PixelFormat.YCbCr_420_SP) { +            requiredSize = height * mStrides[0] +(height >> 1) * mStrides[1]; +        } else { +            requiredSize = height * mStrides[0]; +        } + +        if (requiredSize > mData.length) { +            throw new IllegalArgumentException( +                    "width or/and height is larger than the yuv data"); +        } + +        return true; +    } + +    //////////// native methods + +    private static native boolean nativeCompressToJpeg(byte[] oriYuv, +            int format, int width, int height, int[] offsets, int[] strides, +            int quality, OutputStream stream, byte[] tempStorage); +} diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h index b0fc4b2c04fd..30b7ad92ac80 100644 --- a/include/media/stagefright/CachingDataSource.h +++ b/include/media/stagefright/CachingDataSource.h @@ -33,6 +33,8 @@ public:      virtual ssize_t readAt(off_t offset, void *data, size_t size); +    virtual uint32_t flags(); +  protected:      virtual ~CachingDataSource(); diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index f88666a7432d..0c0ace023171 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -31,6 +31,10 @@ class String8;  class DataSource : public RefBase {  public: +    enum Flags { +        kWantsPrefetching = 1, +    }; +      static sp<DataSource> CreateFromURI(const char *uri);      DataSource() {} @@ -45,6 +49,10 @@ public:      // May return ERROR_UNSUPPORTED.      virtual status_t getSize(off_t *size); +    virtual uint32_t flags() { +        return 0; +    } +      ////////////////////////////////////////////////////////////////////////////      bool sniff(String8 *mimeType, float *confidence); diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h index d5dc9e6c8886..3075f1c82f5b 100644 --- a/include/media/stagefright/HTTPDataSource.h +++ b/include/media/stagefright/HTTPDataSource.h @@ -33,6 +33,10 @@ public:      virtual ssize_t readAt(off_t offset, void *data, size_t size); +    virtual uint32_t flags() { +        return kWantsPrefetching; +    } +  protected:      virtual ~HTTPDataSource(); @@ -52,6 +56,8 @@ private:      status_t mInitCheck; +    ssize_t sendRangeRequest(size_t offset); +      HTTPDataSource(const HTTPDataSource &);      HTTPDataSource &operator=(const HTTPDataSource &);  }; diff --git a/include/ui/Camera.h b/include/ui/Camera.h index 5219772a1a1f..c506fb89582c 100644 --- a/include/ui/Camera.h +++ b/include/ui/Camera.h @@ -82,6 +82,7 @@ enum {  enum {      CAMERA_CMD_START_SMOOTH_ZOOM     = 1,      CAMERA_CMD_STOP_SMOOTH_ZOOM      = 2, +    CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,  };  // camera fatal errors @@ -209,4 +210,3 @@ private:  }; // namespace android  #endif - diff --git a/include/ui/CameraParameters.h b/include/ui/CameraParameters.h index a5ea133be324..cae06761abe5 100644 --- a/include/ui/CameraParameters.h +++ b/include/ui/CameraParameters.h @@ -29,12 +29,6 @@ public:      CameraParameters(const String8 ¶ms) { unflatten(params); }      ~CameraParameters(); -    enum { -        CAMERA_ORIENTATION_UNKNOWN = 0, -        CAMERA_ORIENTATION_PORTRAIT = 1, -        CAMERA_ORIENTATION_LANDSCAPE = 2, -    }; -      String8 flatten() const;      void unflatten(const String8 ¶ms); @@ -63,9 +57,6 @@ public:      void setPictureFormat(const char *format);      const char *getPictureFormat() const; -    int getOrientation() const; -    void setOrientation(int orientation); -      void dump() const;      status_t dump(int fd, const Vector<String16>& args) const; diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp index 2e0409bc688c..a94f6b9e435c 100644 --- a/libs/ui/CameraParameters.cpp +++ b/libs/ui/CameraParameters.cpp @@ -121,9 +121,6 @@ const char CameraParameters::FOCUS_MODE_INFINITY[] = "infinity";  const char CameraParameters::FOCUS_MODE_MACRO[] = "macro";  const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed"; -static const char* portrait = "portrait"; -static const char* landscape = "landscape"; -  CameraParameters::CameraParameters()                  : mMap()  { @@ -282,23 +279,6 @@ void CameraParameters::setPreviewFormat(const char *format)      set(KEY_PREVIEW_FORMAT, format);  } -int CameraParameters::getOrientation() const -{ -    const char* orientation = get("orientation"); -    if (orientation && !strcmp(orientation, portrait)) -        return CAMERA_ORIENTATION_PORTRAIT; -    return CAMERA_ORIENTATION_LANDSCAPE; -} - -void CameraParameters::setOrientation(int orientation) -{ -    if (orientation == CAMERA_ORIENTATION_PORTRAIT) { -        set("orientation", portrait); -    } else { -        set("orientation", landscape); -    } -} -  const char *CameraParameters::getPreviewFormat() const  {      return get(KEY_PREVIEW_FORMAT); diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk new file mode 100644 index 000000000000..902cd96067e9 --- /dev/null +++ b/location/tests/locationtests/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_PACKAGE_NAME := FrameworksLocationTests + +include $(BUILD_PACKAGE) + diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml new file mode 100644 index 000000000000..1d9df0f93a05 --- /dev/null +++ b/location/tests/locationtests/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at +   +          http://www.apache.org/licenses/LICENSE-2.0 +   +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" +          package="com.android.frameworks.locationtests"> + +    <!-- location test permissions --> +    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> +    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> +    <uses-permission android:name="android.permission.WRITE_SETTINGS"/> +    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> +    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> +     +    <application> +        <uses-library android:name="android.test.runner" /> +    </application> + +    <instrumentation +    	android:name="android.test.InstrumentationTestRunner" +    	android:targetPackage="com.android.frameworks.locationtests" +    	android:label="Frameworks Location Tests" /> +</manifest> diff --git a/tests/CoreTests/android/location/LocationTest.java b/location/tests/locationtests/src/android/location/LocationTest.java index 847ac7a000be..847ac7a000be 100644 --- a/tests/CoreTests/android/location/LocationTest.java +++ b/location/tests/locationtests/src/android/location/LocationTest.java diff --git a/media/jni/Android.mk b/media/jni/Android.mk index ca6230f7f87b..1d82e321afc6 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -1,6 +1,10 @@  LOCAL_PATH:= $(call my-dir)  include $(CLEAR_VARS) +ifneq ($(BUILD_WITHOUT_PV),true) +include external/opencore/Config.mk +endif +  LOCAL_SRC_FILES:= \      android_media_MediaPlayer.cpp \      android_media_MediaRecorder.cpp \ @@ -19,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := \      libcutils  ifneq ($(BUILD_WITHOUT_PV),true) +  LOCAL_SRC_FILES += \      android_media_AmrInputStream.cpp diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 3813907e8710..dbb52c63c63f 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -31,6 +31,7 @@ LOCAL_SRC_FILES +=                \          MPEG4Extractor.cpp        \          MPEG4Writer.cpp           \          MediaExtractor.cpp        \ +        Prefetcher.cpp            \          SampleIterator.cpp        \          SampleTable.cpp           \          ShoutcastSource.cpp       \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index f6cd46ac87b5..42b9acce1cfb 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -19,6 +19,7 @@  #include <utils/Log.h>  #include "include/AwesomePlayer.h" +#include "include/Prefetcher.h"  #include "include/SoftwareRenderer.h"  #include <binder/IPCThreadState.h> @@ -118,6 +119,8 @@ AwesomePlayer::AwesomePlayer()      mVideoEventPending = false;      mStreamDoneEvent = new AwesomeEvent(this, 1);      mStreamDoneEventPending = false; +    mBufferingEvent = new AwesomeEvent(this, 2); +    mBufferingEventPending = false;      mQueue.start(); @@ -132,11 +135,16 @@ AwesomePlayer::~AwesomePlayer() {      mClient.disconnect();  } -void AwesomePlayer::cancelPlayerEvents() { +void AwesomePlayer::cancelPlayerEvents(bool keepBufferingGoing) {      mQueue.cancelEvent(mVideoEvent->eventID());      mVideoEventPending = false;      mQueue.cancelEvent(mStreamDoneEvent->eventID());      mStreamDoneEventPending = false; + +    if (!keepBufferingGoing) { +        mQueue.cancelEvent(mBufferingEvent->eventID()); +        mBufferingEventPending = false; +    }  }  void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) { @@ -149,12 +157,22 @@ status_t AwesomePlayer::setDataSource(const char *uri) {      reset_l(); -    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri); +    sp<DataSource> dataSource = DataSource::CreateFromURI(uri); + +    if (dataSource == NULL) { +        return UNKNOWN_ERROR; +    } + +    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);      if (extractor == NULL) {          return UNKNOWN_ERROR;      } +    if (dataSource->flags() & DataSource::kWantsPrefetching) { +        mPrefetcher = new Prefetcher; +    } +      return setDataSource_l(extractor);  } @@ -182,8 +200,6 @@ status_t AwesomePlayer::setDataSource(  }  status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { -    reset_l(); -      bool haveAudio = false;      bool haveVideo = false;      for (size_t i = 0; i < extractor->countTracks(); ++i) { @@ -253,6 +269,8 @@ void AwesomePlayer::reset_l() {      mSeeking = false;      mSeekTimeUs = 0; + +    mPrefetcher.clear();  }  // static @@ -278,13 +296,35 @@ void AwesomePlayer::AudioNotify(void *_me, int what) {      }  } -void AwesomePlayer::notifyListener_l(int msg) { +void AwesomePlayer::notifyListener_l(int msg, int ext1) {      if (mListener != NULL) {          sp<MediaPlayerBase> listener = mListener.promote();          if (listener != NULL) { -            listener->sendEvent(msg); +            listener->sendEvent(msg, ext1); +        } +    } +} + +void AwesomePlayer::onBufferingUpdate() { +    Mutex::Autolock autoLock(mLock); +    mBufferingEventPending = false; + +    if (mDurationUs >= 0) { +        int64_t cachedDurationUs = mPrefetcher->getCachedDurationUs(); +        int64_t positionUs = 0; +        if (mVideoRenderer != NULL) { +            positionUs = mVideoTimeUs; +        } else if (mAudioPlayer != NULL) { +            positionUs = mAudioPlayer->getMediaTimeUs();          } + +        cachedDurationUs += positionUs; + +        double percentage = (double)cachedDurationUs / mDurationUs; +        notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0); + +        postBufferingEvent_l();      }  } @@ -361,6 +401,8 @@ status_t AwesomePlayer::play() {          seekAudioIfNecessary_l();      } +    postBufferingEvent_l(); +      return OK;  } @@ -414,7 +456,7 @@ status_t AwesomePlayer::pause_l() {          return OK;      } -    cancelPlayerEvents(); +    cancelPlayerEvents(true /* keepBufferingGoing */);      if (mAudioPlayer != NULL) {          mAudioPlayer->pause(); @@ -518,11 +560,15 @@ status_t AwesomePlayer::getVideoDimensions(      return OK;  } -status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) { +status_t AwesomePlayer::setAudioSource(sp<MediaSource> source) {      if (source == NULL) {          return UNKNOWN_ERROR;      } +    if (mPrefetcher != NULL) { +        source = mPrefetcher->addSource(source); +    } +      sp<MetaData> meta = source->getFormat();      const char *mime; @@ -549,11 +595,15 @@ status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) {      return mAudioSource != NULL ? OK : UNKNOWN_ERROR;  } -status_t AwesomePlayer::setVideoSource(const sp<MediaSource> &source) { +status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {      if (source == NULL) {          return UNKNOWN_ERROR;      } +    if (mPrefetcher != NULL) { +        source = mPrefetcher->addSource(source); +    } +      mVideoSource = OMXCodec::Create(              mClient.interface(), source->getFormat(),              false, // createEncoder @@ -580,9 +630,13 @@ void AwesomePlayer::onEvent(int32_t code) {      if (code == 1) {          onStreamDone();          return; +    } else if (code == 2) { +        onBufferingUpdate(); +        return;      }      Mutex::Autolock autoLock(mLock); +      mVideoEventPending = false;      if (mSeeking) { @@ -718,5 +772,17 @@ void AwesomePlayer::postStreamDoneEvent_l() {      mQueue.postEvent(mStreamDoneEvent);  } +void AwesomePlayer::postBufferingEvent_l() { +    if (mPrefetcher == NULL) { +        return; +    } + +    if (mBufferingEventPending) { +        return; +    } +    mBufferingEventPending = true; +    mQueue.postEventWithDelay(mBufferingEvent, 1000000ll); +} +  }  // namespace android diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp index 23f48978b5e6..8d04ead950d6 100644 --- a/media/libstagefright/CachingDataSource.cpp +++ b/media/libstagefright/CachingDataSource.cpp @@ -65,6 +65,10 @@ status_t CachingDataSource::initCheck() const {      return mSource->initCheck();  } +uint32_t CachingDataSource::flags() { +    return mSource->flags(); +} +  ssize_t CachingDataSource::readAt(off_t offset, void *data, size_t size) {      Mutex::Autolock autoLock(mLock); diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp index 7e8bbc692237..135a0448c08a 100644 --- a/media/libstagefright/HTTPDataSource.cpp +++ b/media/libstagefright/HTTPDataSource.cpp @@ -190,30 +190,12 @@ HTTPDataSource::~HTTPDataSource() {      mHttp = NULL;  } -ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { -    if (offset >= mBufferOffset -            && offset < (off_t)(mBufferOffset + mBufferLength)) { -        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset); - -        size_t copy = num_bytes_available; -        if (copy > size) { -            copy = size; -        } - -        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy); - -        return copy; -    } - -    mBufferOffset = offset; -    mBufferLength = 0; - +ssize_t HTTPDataSource::sendRangeRequest(size_t offset) {      char host[128];      sprintf(host, "Host: %s\r\n", mHost);      char range[128]; -    sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n", -            mBufferOffset, mBufferOffset + kBufferSize - 1); +    sprintf(range, "Range: bytes=%d-\r\n\r\n", offset);      int http_status; @@ -251,12 +233,44 @@ ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {      char *end;      unsigned long contentLength = strtoul(value.c_str(), &end, 10); -    ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength); +    return contentLength; +} -    if (num_bytes_received <= 0) { -        return num_bytes_received; +ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { +    if (offset >= mBufferOffset +            && offset < (off_t)(mBufferOffset + mBufferLength)) { +        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset); + +        size_t copy = num_bytes_available; +        if (copy > size) { +            copy = size; +        } + +        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy); + +        return copy;      } +    ssize_t contentLength = 0; +    if (mBufferLength <= 0 || offset != mBufferOffset + mBufferLength) { +        mHttp->disconnect(); +        contentLength = sendRangeRequest(offset); + +        if (contentLength > kBufferSize) { +            contentLength = kBufferSize; +        } +    } else { +        contentLength = kBufferSize; +    } + +    mBufferOffset = offset; + +    if (contentLength <= 0) { +        return contentLength; +    } + +    ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength); +      mBufferLength = (size_t)num_bytes_received;      size_t copy = mBufferLength; diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp new file mode 100644 index 000000000000..862998a708ce --- /dev/null +++ b/media/libstagefright/Prefetcher.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Prefetcher" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include "include/Prefetcher.h" + +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <utils/List.h> + +namespace android { + +struct PrefetchedSource : public MediaSource { +    PrefetchedSource( +            const sp<Prefetcher> &prefetcher, +            size_t index, +            const sp<MediaSource> &source); + +    virtual status_t start(MetaData *params); +    virtual status_t stop(); + +    virtual status_t read( +            MediaBuffer **buffer, const ReadOptions *options); + +    virtual sp<MetaData> getFormat(); + +protected: +    virtual ~PrefetchedSource(); + +private: +    friend struct Prefetcher; + +    Mutex mLock; +    Condition mCondition; + +    sp<Prefetcher> mPrefetcher; +    sp<MediaSource> mSource; +    size_t mIndex; +    bool mStarted; +    bool mReachedEOS; +    int64_t mSeekTimeUs; +    int64_t mCacheDurationUs; + +    List<MediaBuffer *> mCachedBuffers; + +    // Returns true iff source is currently caching. +    bool getCacheDurationUs(int64_t *durationUs); + +    void updateCacheDuration_l(); +    void clearCache_l(); + +    void cacheMore(); + +    PrefetchedSource(const PrefetchedSource &); +    PrefetchedSource &operator=(const PrefetchedSource &); +}; + +Prefetcher::Prefetcher() +    : mDone(false), +      mThreadExited(false) { +    startThread(); +} + +Prefetcher::~Prefetcher() { +    stopThread(); +} + +sp<MediaSource> Prefetcher::addSource(const sp<MediaSource> &source) { +    Mutex::Autolock autoLock(mLock); + +    sp<PrefetchedSource> psource = +        new PrefetchedSource(this, mSources.size(), source); + +    mSources.add(psource); + +    return psource; +} + +void Prefetcher::startThread() { +    mThreadExited = false; +    mDone = false; + +    int res = androidCreateThreadEtc( +            ThreadWrapper, this, "Prefetcher", +            ANDROID_PRIORITY_DEFAULT, 0, &mThread); + +    CHECK_EQ(res, 1); +} + +void Prefetcher::stopThread() { +    Mutex::Autolock autoLock(mLock); + +    while (!mThreadExited) { +        mDone = true; +        mCondition.signal(); +        mCondition.wait(mLock); +    } +} + +// static +int Prefetcher::ThreadWrapper(void *me) { +    static_cast<Prefetcher *>(me)->threadFunc(); + +    return 0; +} + +// Cache about 10secs for each source. +static int64_t kMaxCacheDurationUs = 10000000ll; + +void Prefetcher::threadFunc() { +    for (;;) { +        Mutex::Autolock autoLock(mLock); +        if (mDone) { +            mThreadExited = true; +            mCondition.signal(); +            break; +        } +        mCondition.waitRelative(mLock, 10000000ll); + +        int64_t minCacheDurationUs = -1; +        ssize_t minIndex = -1; +        for (size_t i = 0; i < mSources.size(); ++i) { +            sp<PrefetchedSource> source = mSources[i].promote(); + +            if (source == NULL) { +                continue; +            } + +            int64_t cacheDurationUs; +            if (!source->getCacheDurationUs(&cacheDurationUs)) { +                continue; +            } + +            if (cacheDurationUs >= kMaxCacheDurationUs) { +                continue; +            } + +            if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) { +                minCacheDurationUs = cacheDurationUs; +                minIndex = i; +            } +        } + +        if (minIndex < 0) { +            continue; +        } + +        sp<PrefetchedSource> source = mSources[minIndex].promote(); +        if (source != NULL) { +            source->cacheMore(); +        } +    } +} + +int64_t Prefetcher::getCachedDurationUs() { +    Mutex::Autolock autoLock(mLock); + +    int64_t minCacheDurationUs = -1; +    ssize_t minIndex = -1; +    for (size_t i = 0; i < mSources.size(); ++i) { +        int64_t cacheDurationUs; +        sp<PrefetchedSource> source = mSources[i].promote(); +        if (source == NULL) { +            continue; +        } + +        if (!source->getCacheDurationUs(&cacheDurationUs)) { +            continue; +        } + +        if (cacheDurationUs >= kMaxCacheDurationUs) { +            continue; +        } + +        if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) { +            minCacheDurationUs = cacheDurationUs; +            minIndex = i; +        } +    } + +    return minCacheDurationUs < 0 ? 0 : minCacheDurationUs; +} + +//////////////////////////////////////////////////////////////////////////////// + +PrefetchedSource::PrefetchedSource( +        const sp<Prefetcher> &prefetcher, +        size_t index, +        const sp<MediaSource> &source) +    : mPrefetcher(prefetcher), +      mSource(source), +      mIndex(index), +      mStarted(false), +      mReachedEOS(false), +      mSeekTimeUs(0), +      mCacheDurationUs(0) { +} + +PrefetchedSource::~PrefetchedSource() { +    if (mStarted) { +        stop(); +    } +} + +status_t PrefetchedSource::start(MetaData *params) { +    Mutex::Autolock autoLock(mLock); + +    status_t err = mSource->start(params); + +    if (err != OK) { +        return err; +    } + +    mStarted = true; + +    for (;;) { +        // Buffer 2 secs on startup. +        if (mReachedEOS || mCacheDurationUs > 2000000) { +            break; +        } + +        mCondition.wait(mLock); +    } + +    return OK; +} + +status_t PrefetchedSource::stop() { +    Mutex::Autolock autoLock(mLock); + +    clearCache_l(); + +    status_t err = mSource->stop(); + +    mStarted = false; + +    return err; +} + +status_t PrefetchedSource::read( +        MediaBuffer **out, const ReadOptions *options) { +    *out = NULL; + +    Mutex::Autolock autoLock(mLock); + +    CHECK(mStarted); + +    int64_t seekTimeUs; +    if (options && options->getSeekTo(&seekTimeUs)) { +        CHECK(seekTimeUs >= 0); + +        clearCache_l(); + +        mReachedEOS = false; +        mSeekTimeUs = seekTimeUs; +    } + +    while (!mReachedEOS && mCachedBuffers.empty()) { +        mCondition.wait(mLock); +    } + +    if (mCachedBuffers.empty()) { +        return ERROR_END_OF_STREAM; +    } + +    *out = *mCachedBuffers.begin(); +    mCachedBuffers.erase(mCachedBuffers.begin()); +    updateCacheDuration_l(); + +    return OK; +} + +sp<MetaData> PrefetchedSource::getFormat() { +    return mSource->getFormat(); +} + +bool PrefetchedSource::getCacheDurationUs(int64_t *durationUs) { +    Mutex::Autolock autoLock(mLock); + +    if (!mStarted || mReachedEOS) { +        *durationUs = 0; + +        return false; +    } + +    *durationUs = mCacheDurationUs; + +    return true; +} + +void PrefetchedSource::cacheMore() { +    MediaSource::ReadOptions options; + +    { +        Mutex::Autolock autoLock(mLock); + +        if (!mStarted) { +            return; +        } + +        if (mSeekTimeUs >= 0) { +            options.setSeekTo(mSeekTimeUs); +            mSeekTimeUs = -1; +        } +    } + +    MediaBuffer *buffer; +    status_t err = mSource->read(&buffer, &options); + +    Mutex::Autolock autoLock(mLock); + +    if (err != OK) { +        mReachedEOS = true; +        mCondition.signal(); + +        return; +    } + +    CHECK(buffer != NULL); + +    MediaBuffer *copy = new MediaBuffer(buffer->range_length()); +    memcpy(copy->data(), +           (const uint8_t *)buffer->data() + buffer->range_offset(), +           buffer->range_length()); + +    sp<MetaData> from = buffer->meta_data(); +    sp<MetaData> to = copy->meta_data(); + +    int64_t timeUs; +    if (from->findInt64(kKeyTime, &timeUs)) { +        to->setInt64(kKeyTime, timeUs); +    } + +    buffer->release(); +    buffer = NULL; + +    mCachedBuffers.push_back(copy); +    updateCacheDuration_l(); +    mCondition.signal(); +} + +void PrefetchedSource::updateCacheDuration_l() { +    if (mCachedBuffers.size() < 2) { +        mCacheDurationUs = 0; +    } else { +        int64_t firstTimeUs, lastTimeUs; +        CHECK((*mCachedBuffers.begin())->meta_data()->findInt64( +                    kKeyTime, &firstTimeUs)); +        CHECK((*--mCachedBuffers.end())->meta_data()->findInt64( +                    kKeyTime, &lastTimeUs)); + +        mCacheDurationUs = lastTimeUs - firstTimeUs; +    } +} + +void PrefetchedSource::clearCache_l() { +    List<MediaBuffer *>::iterator it = mCachedBuffers.begin(); +    while (it != mCachedBuffers.end()) { +        (*it)->release(); + +        it = mCachedBuffers.erase(it); +    } + +    updateCacheDuration_l(); +} + +}  // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index b28a12c9ce1b..c2e46c0cb75d 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -26,10 +26,11 @@  namespace android { +struct AudioPlayer;  struct MediaBuffer;  struct MediaExtractor;  struct MediaSource; -struct AudioPlayer; +struct Prefetcher;  struct TimeSource;  struct AwesomeRenderer : public RefBase { @@ -109,13 +110,18 @@ private:      bool mVideoEventPending;      sp<TimedEventQueue::Event> mStreamDoneEvent;      bool mStreamDoneEventPending; +    sp<TimedEventQueue::Event> mBufferingEvent; +    bool mBufferingEventPending;      void postVideoEvent_l(int64_t delayUs = -1); +    void postBufferingEvent_l();      void postStreamDoneEvent_l();      MediaBuffer *mLastVideoBuffer;      MediaBuffer *mVideoBuffer; +    sp<Prefetcher> mPrefetcher; +      status_t setDataSource_l(const sp<MediaExtractor> &extractor);      void reset_l();      status_t seekTo_l(int64_t timeUs); @@ -123,17 +129,19 @@ private:      void initRenderer_l();      void seekAudioIfNecessary_l(); -    void cancelPlayerEvents(); +    void cancelPlayerEvents(bool keepBufferingGoing = false); -    status_t setAudioSource(const sp<MediaSource> &source); -    status_t setVideoSource(const sp<MediaSource> &source); +    status_t setAudioSource(sp<MediaSource> source); +    status_t setVideoSource(sp<MediaSource> source);      void onEvent(int32_t code);      static void AudioNotify(void *me, int what);      void onStreamDone(); -    void notifyListener_l(int msg); +    void notifyListener_l(int msg, int ext1 = 0); + +    void onBufferingUpdate();      AwesomePlayer(const AwesomePlayer &);      AwesomePlayer &operator=(const AwesomePlayer &); diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h new file mode 100644 index 000000000000..7a977855d416 --- /dev/null +++ b/media/libstagefright/include/Prefetcher.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PREFETCHER_H_ + +#define PREFETCHER_H_ + +#include <utils/RefBase.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace android { + +struct MediaSource; +struct PrefetchedSource; + +struct Prefetcher : public RefBase { +    Prefetcher(); + +    // Given an existing MediaSource returns a new MediaSource +    // that will benefit from prefetching/caching the original one. +    sp<MediaSource> addSource(const sp<MediaSource> &source); + +    int64_t getCachedDurationUs(); + +protected: +    virtual ~Prefetcher(); + +private: +    Mutex mLock; +    Condition mCondition; + +    Vector<wp<PrefetchedSource> > mSources; +    android_thread_id_t mThread; +    bool mDone; +    bool mThreadExited; + +    void startThread(); +    void stopThread(); + +    static int ThreadWrapper(void *me); +    void threadFunc(); + +    Prefetcher(const Prefetcher &); +    Prefetcher &operator=(const Prefetcher &); +}; + +}  // namespace android + +#endif  // PREFETCHER_H_ diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 5127255d8779..608726829762 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -274,6 +274,7 @@ public class MediaNames {      //video thumbnail      public static final String THUMBNAIL_OUTPUT = "/sdcard/media_api/videoThumbnail.png";      public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/media_api/goldenThumbnail.png"; +    public static final String GOLDEN_THUMBNAIL_OUTPUT_2 = "/sdcard/media_api/goldenThumbnail2.png";      //Metadata Utility      public static final String[] THUMBNAIL_CAPTURE_TEST_FILES = { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 4e30f913c935..ee6067aba3e8 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -420,7 +420,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra      @LargeTest      public void testGetThumbnail() throws Exception { -      boolean getThumbnail = CodecTest.getThumbnail(MediaNames.VIDEO_H264_AAC, MediaNames.GOLDEN_THUMBNAIL_OUTPUT); +      boolean getThumbnail = CodecTest.getThumbnail(MediaNames.VIDEO_H264_AAC, MediaNames.GOLDEN_THUMBNAIL_OUTPUT_2);        assertTrue("Get Thumbnail", getThumbnail);               } diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk new file mode 100755 index 000000000000..2f1d6abd8fa2 --- /dev/null +++ b/packages/DefaultContainerService/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := DefaultContainerService + +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml new file mode 100755 index 000000000000..fd77148a24ed --- /dev/null +++ b/packages/DefaultContainerService/AndroidManifest.xml @@ -0,0 +1,14 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" +        package="com.android.defcontainer"> +    <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/> + +    <application android:process="def.container.service" +                 android:label="@string/service_name"> + +        <service android:name=".DefaultContainerService" +                 android:enabled="true" +                 android:exported="true" +                 android:permission="android.permission.COPY_PROTECTED_DATA"/> +    </application> + +</manifest> diff --git a/packages/DefaultContainerService/res/values/strings.xml b/packages/DefaultContainerService/res/values/strings.xml new file mode 100644 index 000000000000..2897f34d5845 --- /dev/null +++ b/packages/DefaultContainerService/res/values/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> +    <!-- service name  --> +    <string name="service_name">Media Container Service</string> +</resources> diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java new file mode 100644 index 000000000000..d36207bfdc31 --- /dev/null +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -0,0 +1,305 @@ +package com.android.defcontainer; + +import com.android.internal.app.IMediaContainerService; + +import android.content.Intent; +import android.net.Uri; +import android.os.Debug; +import android.os.IBinder; +import android.os.IMountService; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.app.Service; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.os.FileUtils; + + +/* + * This service copies a downloaded apk to a file passed in as + * a ParcelFileDescriptor or to a newly created container specified + * by parameters. The DownloadManager gives access to this process + * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER + * permission to access apks downloaded via the download manager. + */ +public class DefaultContainerService extends Service { +    private static final String TAG = "DefContainer"; +    private static final boolean localLOGV = false; + +    private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { +        /* +         * Creates a new container and copies resource there. +         * @param paackageURI the uri of resource to be copied. Can be either +         * a content uri or a file uri +         * @param containerId the id of the secure container that should +         * be used for creating a secure container into which the resource +         * will be copied. +         * @param key Refers to key used for encrypting the secure container +         * @param resFileName Name of the target resource file(relative to newly +         * created secure container) +         * @return Returns the new cache path where the resource has been copied into +         * +         */ +        public String copyResourceToContainer(final Uri packageURI, +                final String containerId, +                final String key, final String resFileName) { +            if (packageURI == null || containerId == null) { +                return null; +            } +            return copyResourceInner(packageURI, containerId, key, resFileName); +        } + +        /* +         * Copy specified resource to output stream +         * @param packageURI the uri of resource to be copied. Should be a +         * file uri +         * @param outStream Remote file descriptor to be used for copying +         * @return Returns true if copy succeded or false otherwise. +         */ +        public boolean copyResource(final Uri packageURI, +                ParcelFileDescriptor outStream) { +            if (packageURI == null ||  outStream == null) { +                return false; +            } +            ParcelFileDescriptor.AutoCloseOutputStream +            autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); +            return copyFile(packageURI, autoOut); +        } +    }; + +    public IBinder onBind(Intent intent) { +        return mBinder; +    } + +    private IMountService getMountService() { +        return IMountService.Stub.asInterface(ServiceManager.getService("mount")); +    } + +    private String copyResourceInner(Uri packageURI, String newCacheId, String key, String resFileName) { +        // Create new container at newCachePath +        String codePath = packageURI.getPath(); +        String newCachePath = null; +        final int CREATE_FAILED = 1; +        final int COPY_FAILED = 2; +        final int FINALIZE_FAILED = 3; +        final int PASS = 4; +        int errCode = CREATE_FAILED; +        // Create new container +        if ((newCachePath = createSdDir(packageURI, newCacheId, key)) != null) { +            File resFile = new File(newCachePath, resFileName); +            errCode = COPY_FAILED; +            if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile); +            // Copy file from codePath +            if (FileUtils.copyFile(new File(codePath), resFile)) { +                errCode = FINALIZE_FAILED; +                if (finalizeSdDir(newCacheId)) { +                    errCode = PASS; +                } +            } +        } +        // Print error based on errCode +        String errMsg = ""; +        switch (errCode) { +            case CREATE_FAILED: +                errMsg = "CREATE_FAILED"; +                break; +            case COPY_FAILED: +                errMsg = "COPY_FAILED"; +                destroySdDir(newCacheId); +                break; +            case FINALIZE_FAILED: +                errMsg = "FINALIZE_FAILED"; +                destroySdDir(newCacheId); +                break; +            default: +                errMsg = "PASS"; +                unMountSdDir(newCacheId); +                break; +        } +        Log.i(TAG, "Status: " + errMsg); +        if (errCode != PASS) { +            return null; +        } +        return newCachePath; +    } + +    private String createSdDir(final Uri packageURI, +            String containerId, String sdEncKey) { +        File tmpPackageFile = new File(packageURI.getPath()); +        // Create mount point via MountService +        IMountService mountService = getMountService(); +        long len = tmpPackageFile.length(); +        int mbLen = (int) (len/(1024*1024)); +        if ((len - (mbLen * 1024 * 1024)) > 0) { +            mbLen++; +        } +        if (localLOGV) Log.i(TAG, "mbLen="+mbLen); +        String cachePath = null; +        int ownerUid = Process.myUid(); +        try { +            cachePath = mountService.createSecureContainer(containerId, +                mbLen, +                "vfat", sdEncKey, ownerUid); +            if (localLOGV) Log.i(TAG, "Trying to create secure container for  " +                    + containerId + ", cachePath =" + cachePath); +            return cachePath; +        } catch(IllegalStateException e) { +            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +            return null; +        } +        // TODO just fail here and let the user delete later on. +        try { +            mountService.destroySecureContainer(containerId); +            if (localLOGV) Log.i(TAG, "Destroying cache for " + containerId +                    + ", cachePath =" + cachePath); +        } catch(IllegalStateException e) { +            Log.e(TAG, "Failed to destroy existing cache: " + e); +            return null; +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +            return null; +        } +       try { +            cachePath = mountService.createSecureContainer(containerId, +                mbLen, +                "vfat", sdEncKey, ownerUid); +            if (localLOGV) Log.i(TAG, "Trying to install again " + containerId +                   + ", cachePath =" + cachePath); +            return cachePath; +        } catch(IllegalStateException e) { +            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +        } +        return null; +    } + +    private boolean destroySdDir(String containerId) { +        try { +            // We need to destroy right away +            getMountService().destroySecureContainer(containerId); +            return true; +        } catch (IllegalStateException e) { +            Log.i(TAG, "Failed to destroy container : " + containerId); +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +        } +        return false; +    } + +    private boolean finalizeSdDir(String containerId){ +        try { +            getMountService().finalizeSecureContainer(containerId); +            return true; +        } catch (IllegalStateException e) { +            Log.i(TAG, "Failed to finalize container for pkg : " + containerId); +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +        } +        return false; +    } + +    private boolean unMountSdDir(String containerId) { +        try { +            getMountService().unmountSecureContainer(containerId); +            return true; +        } catch (IllegalStateException e) { +            Log.e(TAG, "Failed to unmount id:  " + containerId + " with exception " + e); +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +        } +        return false; +    } + +    private String mountSdDir(String containerId, String key) { +        try { +            return getMountService().mountSecureContainer(containerId, key, Process.myUid()); +        } catch (IllegalStateException e) { +            Log.e(TAG, "Failed to mount id: " + +                    containerId + " with exception " + e); +        } catch(RemoteException e) { +            Log.e(TAG, "MounteService not running?"); +        } +        return null; +    } + +    public static boolean copyToFile(InputStream inputStream, FileOutputStream out) { +        try { +            byte[] buffer = new byte[4096]; +            int bytesRead; +            while ((bytesRead = inputStream.read(buffer)) >= 0) { +                out.write(buffer, 0, bytesRead); +            } +            return true; +        } catch (IOException e) { +            Log.i(TAG, "Exception : " + e + " when copying file"); +            return false; +        } +    } + +    public static boolean copyToFile(File srcFile, FileOutputStream out) { +        InputStream inputStream = null; +        try { +            inputStream = new FileInputStream(srcFile); +            return copyToFile(inputStream, out); +        } catch (IOException e) { +            return false; +        } finally { +            try { if (inputStream != null) inputStream.close(); } catch (IOException e) {} +        } +    } + +    private  boolean copyFile(Uri pPackageURI, FileOutputStream outStream) { +        if (pPackageURI.getScheme().equals("file")) { +            final File srcPackageFile = new File(pPackageURI.getPath()); +            // We copy the source package file to a temp file and then rename it to the +            // destination file in order to eliminate a window where the package directory +            // scanner notices the new package file but it's not completely copied yet. +            if (!copyToFile(srcPackageFile, outStream)) { +                Log.e(TAG, "Couldn't copy file: " + srcPackageFile); +                return false; +            } +        } else if (pPackageURI.getScheme().equals("content")) { +            ParcelFileDescriptor fd = null; +            try { +                fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); +            } catch (FileNotFoundException e) { +                Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e); +                return false; +            } +            if (fd == null) { +                Log.e(TAG, "Couldn't open file descriptor from download service (null)."); +                return false; +            } else { +                if (localLOGV) { +                    Log.v(TAG, "Opened file descriptor from download service."); +                } +                ParcelFileDescriptor.AutoCloseInputStream +                dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); +                // We copy the source package file to a temp file and then rename it to the +                // destination file in order to eliminate a window where the package directory +                // scanner notices the new package file but it's not completely copied yet. +                if (!copyToFile(dlStream, outStream)) { +                    Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file."); +                    return false; +                } +            } +        } else { +            Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); +            return false; +        } +        return true; +    } +} diff --git a/preloaded-classes b/preloaded-classes index 90bbb3746c46..d108883a4782 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -694,9 +694,7 @@ com.ibm.icu4jni.charset.CharsetDecoderICU  com.ibm.icu4jni.charset.CharsetEncoderICU  com.ibm.icu4jni.charset.CharsetICU  com.ibm.icu4jni.text.CollationAttribute -com.ibm.icu4jni.text.DecimalFormat -com.ibm.icu4jni.text.DecimalFormatSymbols -com.ibm.icu4jni.text.NativeDecimalFormat$UNumberFormatAttribute +com.ibm.icu4jni.text.NativeDecimalFormat  com.ibm.icu4jni.text.RuleBasedCollator  com.ibm.icu4jni.util.Resources$DefaultTimeZones  dalvik.system.DexFile diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java index debbbb4ac109..fbb4411838f6 100644 --- a/services/java/com/android/server/BootReceiver.java +++ b/services/java/com/android/server/BootReceiver.java @@ -89,14 +89,14 @@ public class BootReceiver extends BroadcastReceiver {          }          ContentResolver cr = context.getContentResolver(); -        logBootFile(cr, db, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG"); -        logBootFile(cr, db, "/proc/last_kmsg", "SYSTEM_LAST_KMSG"); -        logBootFile(cr, db, "/data/dontpanic/apanic_console", "APANIC_CONSOLE"); -        logBootFile(cr, db, "/data/dontpanic/apanic_threads", "APANIC_THREADS"); +        logBootFile(cr, db, props, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG"); +        logBootFile(cr, db, props, "/proc/last_kmsg", "SYSTEM_LAST_KMSG"); +        logBootFile(cr, db, props, "/data/dontpanic/apanic_console", "APANIC_CONSOLE"); +        logBootFile(cr, db, props, "/data/dontpanic/apanic_threads", "APANIC_THREADS");      } -    private void logBootFile(ContentResolver cr, DropBoxManager db, String filename, String tag) -            throws IOException { +    private void logBootFile(ContentResolver cr, DropBoxManager db, +            CharSequence headers, String filename, String tag) throws IOException {          if (cr == null || db == null || !db.isTagEnabled(tag)) return;  // Logging disabled          File file = new File(filename); @@ -108,10 +108,8 @@ public class BootReceiver extends BroadcastReceiver {          if (lastTime == fileTime) return;  // Already logged this particular file          Settings.Secure.putLong(cr, setting, fileTime); -        StringBuilder report = new StringBuilder(); -        report.append("Build: ").append(Build.FINGERPRINT).append("\n"); -        report.append("Kernel: "); -        report.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")); +        StringBuilder report = new StringBuilder(headers); +        report.append("\n");          report.append(FileUtils.readTextFile(new File(filename), LOG_SIZE, "[[TRUNCATED]]\n"));          db.addText(tag, report.toString());      } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index fbd53173fd0b..ebd6f3d0bac3 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -23,19 +23,23 @@ import org.xmlpull.v1.XmlPullParser;  import org.xmlpull.v1.XmlPullParserException;  import org.xmlpull.v1.XmlSerializer; +import android.app.Activity;  import android.app.DeviceAdmin;  import android.app.DeviceAdminInfo;  import android.app.DevicePolicyManager;  import android.app.IDevicePolicyManager; +import android.content.BroadcastReceiver;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent;  import android.content.pm.PackageManager;  import android.content.pm.ResolveInfo;  import android.os.Binder; +import android.os.Handler;  import android.os.IBinder;  import android.os.IPowerManager;  import android.os.RecoverySystem; +import android.os.RemoteCallback;  import android.os.RemoteException;  import android.os.ServiceManager;  import android.util.Log; @@ -64,16 +68,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      ActiveAdmin mActiveAdmin;      static class ActiveAdmin { +        final DeviceAdminInfo info; +         +        int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED; +        int minimumPasswordLength = 0; +        long maximumTimeToUnlock = 0; +        int maximumFailedPasswordsForWipe = 0; +                  ActiveAdmin(DeviceAdminInfo _info) {              info = _info;          } -        final DeviceAdminInfo info;          int getUid() { return info.getActivityInfo().applicationInfo.uid; } -        int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED; -        int minimumPasswordLength = 0; -        long maximumTimeToUnlock = 0; +        void writeToXml(XmlSerializer out) +                throws IllegalArgumentException, IllegalStateException, IOException { +            if (passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) { +                out.startTag(null, "password-mode"); +                out.attribute(null, "value", Integer.toString(passwordMode)); +                out.endTag(null, "password-mode"); +                if (minimumPasswordLength > 0) { +                    out.startTag(null, "min-password-length"); +                    out.attribute(null, "value", Integer.toString(minimumPasswordLength)); +                    out.endTag(null, "mn-password-length"); +                } +            } +            if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) { +                out.startTag(null, "max-time-to-unlock"); +                out.attribute(null, "value", Long.toString(maximumTimeToUnlock)); +                out.endTag(null, "max-time-to-unlock"); +            } +            if (maximumFailedPasswordsForWipe != 0) { +                out.startTag(null, "max-failed-password-wipe"); +                out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe)); +                out.endTag(null, "max-failed-password-wipe"); +            } +        } +         +        void readFromXml(XmlPullParser parser) +                throws XmlPullParserException, IOException { +            int outerDepth = parser.getDepth(); +            int type; +            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT +                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { +                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { +                    continue; +                } +                String tag = parser.getName(); +                if ("password-mode".equals(tag)) { +                    passwordMode = Integer.parseInt( +                            parser.getAttributeValue(null, "value")); +                } else if ("min-password-length".equals(tag)) { +                    minimumPasswordLength = Integer.parseInt( +                            parser.getAttributeValue(null, "value")); +                } else if ("max-time-to-unlock".equals(tag)) { +                    maximumTimeToUnlock = Long.parseLong( +                            parser.getAttributeValue(null, "value")); +                } else if ("max-failed-password-wipe".equals(tag)) { +                    maximumFailedPasswordsForWipe = Integer.parseInt( +                            parser.getAttributeValue(null, "value")); +                } +            } +        }      }      /** @@ -91,25 +147,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          return mIPowerManager;      } -    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy) +    ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) { +        ActiveAdmin admin = mActiveAdmin; +        if (admin != null +                && who.getPackageName().equals(admin.info.getActivityInfo().packageName) +                && who.getClassName().equals(admin.info.getActivityInfo().name)) { +            return admin; +        } +        return null; +    } +     +    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who)              throws SecurityException { -        if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) { +        ActiveAdmin admin = mActiveAdmin; +        if (admin != null && admin.getUid() == Binder.getCallingUid()) {              if (who != null) { -                if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName) -                        || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) { +                if (!who.getPackageName().equals(admin.info.getActivityInfo().packageName) +                        || !who.getClassName().equals(admin.info.getActivityInfo().name)) {                      throw new SecurityException("Current admin is not " + who);                  }              } -            if (!mActiveAdmin.info.usesPolicy(reqPolicy)) { -                throw new SecurityException("Admin " + mActiveAdmin.info.getComponent() -                        + " did not specify uses-policy for: " -                        + mActiveAdmin.info.getTagForPolicy(reqPolicy)); -            }              return mActiveAdmin;          }          throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());      } +    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy) +            throws SecurityException { +        ActiveAdmin admin = getActiveAdminForCallerLocked(who); +        if (!admin.info.usesPolicy(reqPolicy)) { +            throw new SecurityException("Admin " + admin.info.getComponent() +                    + " did not specify uses-policy for: " +                    + admin.info.getTagForPolicy(reqPolicy)); +        } +        return admin; +    } +          void sendAdminCommandLocked(ActiveAdmin admin, String action) {          Intent intent = new Intent(action);          intent.setComponent(admin.info.getComponent()); @@ -182,25 +255,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              if (ap != null) {                  out.startTag(null, "admin");                  out.attribute(null, "name", ap.info.getComponent().flattenToString()); -                if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) { -                    out.startTag(null, "password-mode"); -                    out.attribute(null, "value", Integer.toString(ap.passwordMode)); -                    out.endTag(null, "password-mode"); -                    if (ap.minimumPasswordLength > 0) { -                        out.startTag(null, "min-password-length"); -                        out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength)); -                        out.endTag(null, "mn-password-length"); -                    } -                } -                if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) { -                    out.startTag(null, "max-time-to-unlock"); -                    out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock)); -                    out.endTag(null, "max-time-to-unlock"); -                } +                ap.writeToXml(out);                  out.endTag(null, "admin");              }              out.endTag(null, "policies"); +            if (mFailedPasswordAttempts != 0) { +                out.startTag(null, "failed-password-attempts"); +                out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts)); +                out.endTag(null, "failed-password-attempts"); +            } +                          out.endDocument();              stream.close();              journal.commit(); @@ -220,51 +285,41 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          JournaledFile journal = makeJournaledFile();          FileInputStream stream = null;          File file = journal.chooseForRead(); -        boolean success = false;          try {              stream = new FileInputStream(file);              XmlPullParser parser = Xml.newPullParser();              parser.setInput(stream, null); -            int type = parser.next(); -            while (type != XmlPullParser.START_TAG) { -                type = parser.next(); +            int type; +            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT +                    && type != XmlPullParser.START_TAG) {              }              String tag = parser.getName(); -            if ("policies".equals(tag)) { -                ActiveAdmin ap = null; -                do { -                    type = parser.next(); -                    if (type == XmlPullParser.START_TAG) { -                        tag = parser.getName(); -                        if (ap == null) { -                            if ("admin".equals(tag)) { -                                DeviceAdminInfo dai = findAdmin( -                                        ComponentName.unflattenFromString( -                                                parser.getAttributeValue(null, "name"))); -                                if (dai != null) { -                                    ap = new ActiveAdmin(dai); -                                } -                            } -                        } else if ("password-mode".equals(tag)) { -                            ap.passwordMode = Integer.parseInt( -                                    parser.getAttributeValue(null, "value")); -                        } else if ("min-password-length".equals(tag)) { -                            ap.minimumPasswordLength = Integer.parseInt( -                                    parser.getAttributeValue(null, "value")); -                        } else if ("max-time-to-unlock".equals(tag)) { -                            ap.maximumTimeToUnlock = Long.parseLong( -                                    parser.getAttributeValue(null, "value")); -                        } -                    } else if (type == XmlPullParser.END_TAG) { -                        tag = parser.getName(); -                        if (ap != null && "admin".equals(tag)) { -                            mActiveAdmin = ap; -                            ap = null; -                        } +            if (!"policies".equals(tag)) { +                throw new XmlPullParserException( +                        "Settings do not start with policies tag: found " + tag); +            } +            type = parser.next(); +            int outerDepth = parser.getDepth(); +            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT +                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { +                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { +                    continue; +                } +                tag = parser.getName(); +                if ("admin".equals(tag)) { +                    DeviceAdminInfo dai = findAdmin( +                            ComponentName.unflattenFromString( +                                    parser.getAttributeValue(null, "name"))); +                    if (dai != null) { +                        ActiveAdmin ap = new ActiveAdmin(dai); +                        ap.readFromXml(parser); +                        mActiveAdmin = ap;                      } -                } while (type != XmlPullParser.END_DOCUMENT); -                success = true; +                } else if ("failed-password-attempts".equals(tag)) { +                    mFailedPasswordAttempts = Integer.parseInt( +                            parser.getAttributeValue(null, "value")); +                }              }          } catch (NullPointerException e) {              Log.w(TAG, "failed parsing " + file + " " + e); @@ -285,10 +340,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              // Ignore          } -        if (!success) { -            Log.w(TAG, "No valid start tag found in policies file"); -        } -                  long timeMs = getMaximumTimeToLock();          if (timeMs <= 0) {              timeMs = Integer.MAX_VALUE; @@ -418,6 +469,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }      } +    public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) { +        synchronized (this) { +            // This API can only be called by an active device admin, +            // so try to retrieve it to check that the caller is one. +            getActiveAdminForCallerLocked(who, +                    DeviceAdminInfo.USES_POLICY_WIPE_DATA); +            ActiveAdmin ap = getActiveAdminForCallerLocked(who, +                    DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); +            if (ap.maximumFailedPasswordsForWipe != num) { +                ap.maximumFailedPasswordsForWipe = num; +                saveSettingsLocked(); +            } +        } +    } +     +    public int getMaximumFailedPasswordsForWipe() { +        synchronized (this) { +            return mActiveAdmin != null ? mActiveAdmin.maximumFailedPasswordsForWipe : 0; +        } +    } +          public boolean resetPassword(String password) {          int mode;          synchronized (this) { @@ -488,20 +560,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }      } +    void wipeDataLocked(int flags) { +        try { +            RecoverySystem.rebootWipeUserData(mContext); +        } catch (IOException e) { +            Log.w(TAG, "Failed requesting data wipe", e); +        } +    } +          public void wipeData(int flags) {          synchronized (this) {              // This API can only be called by an active device admin,              // so try to retrieve it to check that the caller is one.              getActiveAdminForCallerLocked(null,                      DeviceAdminInfo.USES_POLICY_WIPE_DATA); +            long ident = Binder.clearCallingIdentity(); +            try { +                wipeDataLocked(flags); +            } finally { +                Binder.restoreCallingIdentity(ident); +            }          } -        long ident = Binder.clearCallingIdentity(); -        try { -            RecoverySystem.rebootWipeUserData(mContext); -        } catch (IOException e) { -            Log.w(TAG, "Failed requesting data wipe", e); -        } finally { -            Binder.restoreCallingIdentity(ident); +    } +     +    public void getRemoveWarning(ComponentName comp, final RemoteCallback result) { +        mContext.enforceCallingOrSelfPermission( +                android.Manifest.permission.BIND_DEVICE_ADMIN, null); +         +        synchronized (this) { +            ActiveAdmin admin = getActiveAdminUncheckedLocked(comp); +            if (admin == null) { +                try { +                    result.sendResult(null); +                } catch (RemoteException e) { +                } +                return; +            } +            Intent intent = new Intent(DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); +            intent.setComponent(admin.info.getComponent()); +            mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { +                @Override +                public void onReceive(Context context, Intent intent) { +                    try { +                        result.sendResult(getResultExtras(false)); +                    } catch (RemoteException e) { +                    } +                } +            }, null, Activity.RESULT_OK, null, null);          }      } @@ -516,7 +621,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {                  try {                      mActivePasswordMode = mode;                      mActivePasswordLength = length; -                    mFailedPasswordAttempts = 0; +                    if (mFailedPasswordAttempts != 0) { +                        mFailedPasswordAttempts = 0; +                        saveSettingsLocked(); +                    }                      sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED,                              DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);                  } finally { @@ -534,6 +642,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              long ident = Binder.clearCallingIdentity();              try {                  mFailedPasswordAttempts++; +                saveSettingsLocked(); +                int max = getMaximumFailedPasswordsForWipe(); +                if (max > 0 && mFailedPasswordAttempts >= max) { +                    wipeDataLocked(0); +                }                  sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED,                          DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);              } finally { @@ -551,6 +664,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {                  long ident = Binder.clearCallingIdentity();                  try {                      mFailedPasswordAttempts = 0; +                    saveSettingsLocked();                      sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED,                              DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);                  } finally { diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java index 6a7d4328bf76..88b1f02aeabb 100644 --- a/services/java/com/android/server/Installer.java +++ b/services/java/com/android/server/Installer.java @@ -263,10 +263,15 @@ class Installer {          return execute(builder.toString());      } -    public int setForwardLockPerm(String packageName, int gid) { +    /* +     * @param packagePathSuffix The name of the path relative to install +     * directory. Say if the path name is /data/app/com.test-1.apk, +     * the package suffix path will be com.test-1 +     */ +    public int setForwardLockPerm(String packagePathSuffix, int gid) {          StringBuilder builder = new StringBuilder("protect");          builder.append(' '); -        builder.append(packageName); +        builder.append(packagePathSuffix);          builder.append(' ');          builder.append(gid);          return execute(builder.toString()); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 85a2e0bf751e..0b7cfaee90de 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -553,7 +553,7 @@ class MountService extends IMountService.Stub                          if (st == VolumeState.NoMedia) {                              state = Environment.MEDIA_REMOVED;                          } else if (st == VolumeState.Idle) { -                            state = Environment.MEDIA_UNMOUNTED; +                            state = null;                              try {                                  mountVolume(path);                              } catch (Exception ex) { @@ -569,7 +569,9 @@ class MountService extends IMountService.Stub                              throw new Exception(String.format("Unexpected state %d", st));                          }                      } -                    updatePublicVolumeState(path, state); +                    if (state != null) { +                        updatePublicVolumeState(path, state); +                    }                  } catch (Exception e) {                      Log.e(TAG, "Error processing initial volume state", e);                      updatePublicVolumeState(path, Environment.MEDIA_REMOVED); @@ -1056,11 +1058,21 @@ class MountService extends IMountService.Stub      }      public String[] getSecureContainerList() throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_ACCESS) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_ACCESS permission"); +        }          return mConnector.doListCommand("list_asec", VoldResponseCode.AsecListResult);      }      public String createSecureContainer(String id, int sizeMb, String fstype,                                      String key, int ownerUid) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_CREATE) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_CREATE permission"); +        }          String cmd = String.format("create_asec %s %d %s %s %d",                                     id, sizeMb, fstype, key, ownerUid);          mConnector.doCommand(cmd); @@ -1068,15 +1080,31 @@ class MountService extends IMountService.Stub      }      public void finalizeSecureContainer(String id) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_CREATE) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_CREATE permission"); +        }          mConnector.doCommand(String.format("finalize_asec %s", id));      }      public void destroySecureContainer(String id) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_DESTROY) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_DESTROY permission"); +        }          mConnector.doCommand(String.format("destroy_asec %s", id));      }      public String mountSecureContainer(String id, String key,                                         int ownerUid) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_MOUNT_UNMOUNT) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission"); +        } +        mConnector.doCommand(String.format("destroy_asec %s", id));          String cmd = String.format("mount_asec %s %s %d",                                     id, key, ownerUid);          mConnector.doCommand(cmd); @@ -1084,16 +1112,31 @@ class MountService extends IMountService.Stub      }      public void unmountSecureContainer(String id) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_MOUNT_UNMOUNT) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission"); +        }          String cmd = String.format("unmount_asec %s", id);          mConnector.doCommand(cmd);      }      public void renameSecureContainer(String oldId, String newId) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_RENAME) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_RENAME permission"); +        }          String cmd = String.format("rename_asec %s %s", oldId, newId);          mConnector.doCommand(cmd);      }      public String getSecureContainerPath(String id) throws IllegalStateException { +        if (mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.ASEC_ACCESS) +                != PackageManager.PERMISSION_GRANTED) { +            throw new SecurityException("Requires ASEC_ACCESS permission"); +        }          ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);          for (String line : rsp) { diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 13079728d504..1cf3badb2555 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -16,6 +16,7 @@  package com.android.server; +import com.android.internal.app.IMediaContainerService;  import com.android.internal.app.ResolverActivity;  import com.android.common.FastXmlSerializer;  import com.android.common.XmlUtils; @@ -31,6 +32,7 @@ import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter;  import android.content.IntentSender; +import android.content.ServiceConnection;  import android.content.IntentSender.SendIntentException;  import android.content.pm.ActivityInfo;  import android.content.pm.ApplicationInfo; @@ -62,6 +64,7 @@ import android.os.Binder;  import android.os.Build;  import android.os.Bundle;  import android.os.HandlerThread; +import android.os.IBinder;  import android.os.Looper;  import android.os.Message;  import android.os.Parcel; @@ -283,26 +286,79 @@ class PackageManagerService extends IPackageManager.Stub {      final HashMap<String, ArrayList<String>> mPendingBroadcasts              = new HashMap<String, ArrayList<String>>();      static final int SEND_PENDING_BROADCAST = 1; -    static final int DESTROY_SD_CONTAINER = 2; +    static final int MCS_BOUND = 3; +    static final int END_COPY = 4; +    static final int INIT_COPY = 5; +    static final int MCS_UNBIND = 6;      // Delay time in millisecs      static final int BROADCAST_DELAY = 10 * 1000; -    static final int DESTROY_SD_CONTAINER_DELAY = 30 * 1000; +    private ServiceConnection mDefContainerConn = new ServiceConnection() { +        public void onServiceConnected(ComponentName name, IBinder service) { +            IMediaContainerService imcs = +                IMediaContainerService.Stub.asInterface(service); +            Message msg = mHandler.obtainMessage(MCS_BOUND, imcs); +            mHandler.sendMessage(msg); +        } + +        public void onServiceDisconnected(ComponentName name) { +        } +    };      class PackageHandler extends Handler { +        final ArrayList<InstallArgs> mPendingInstalls = +            new ArrayList<InstallArgs>(); +        // Service Connection to remote media container service to copy +        // package uri's from external media onto secure containers +        // or internal storage. +        private IMediaContainerService mContainerService = null; +          PackageHandler(Looper looper) {              super(looper);          }          public void handleMessage(Message msg) {              switch (msg.what) { -                case DESTROY_SD_CONTAINER: -                    String pkgName = (String) msg.obj; -                    if (pkgName != null) { -                        // Too bad we cannot handle the errors from destroying the containers. -                        if (!destroySdDir(pkgName)) { -                            Log.e(TAG, "Failed to destroy container for pkg : " + pkgName); +                case INIT_COPY: { +                    InstallArgs args = (InstallArgs) msg.obj; +                    args.createCopyFile(); +                    Intent service = new Intent().setComponent(new ComponentName( +                            "com.android.defcontainer", +                            "com.android.defcontainer.DefaultContainerService")); +                    if (mContainerService != null) { +                        // No need to add to pending list. Use remote stub directly +                        handleStartCopy(args); +                    } else { +                        if (mContext.bindService(service, mDefContainerConn, +                                Context.BIND_AUTO_CREATE)) { +                            mPendingInstalls.add(args); +                        } else { +                            Log.e(TAG, "Failed to bind to media container service"); +                            // Indicate install failure TODO add new error code +                            processPendingInstall(args, +                                    PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE); +                        } +                    } +                    break; +                } +                case MCS_BOUND: { +                    // Initialize mContainerService if needed. +                    if (msg.obj != null) { +                        mContainerService = (IMediaContainerService) msg.obj; +                    } +                    if (mPendingInstalls.size() > 0) { +                        InstallArgs args = mPendingInstalls.remove(0); +                        if (args != null) { +                            handleStartCopy(args);                          }                      }                      break; +                } +                case MCS_UNBIND : { +                    if (mPendingInstalls.size() == 0) { +                        mContext.unbindService(mDefContainerConn); +                        mContainerService = null; +                    } +                    break; +                }                  case SEND_PENDING_BROADCAST : {                      String packages[];                      ArrayList components[]; @@ -343,7 +399,37 @@ class PackageManagerService extends IPackageManager.Stub {                  }              }          } + +        // Utility method to initiate copying apk via media +        // container service. +        private void handleStartCopy(InstallArgs args) { +            int ret = PackageManager.INSTALL_SUCCEEDED; +            if (mContainerService == null) { +                // Install error +                ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; +            } else { +                ret = args.copyApk(mContainerService); +            } +            mHandler.sendEmptyMessage(MCS_UNBIND); +            processPendingInstall(args, ret); +        }      } + +    static boolean installOnSd(int flags) { +        if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) || +                ((flags & PackageManager.INSTALL_ON_SDCARD) == 0)) { +            return false; +        } +        return true; +    } + +    static boolean isFwdLocked(int flags) { +        if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { +            return true; +        } +        return false; +    } +      public static final IPackageManager main(Context context, boolean factoryTest) {          PackageManagerService m = new PackageManagerService(context, factoryTest);          ServiceManager.addService("package", m); @@ -2022,11 +2108,12 @@ class PackageManagerService extends IPackageManager.Stub {              int parseFlags,              int scanMode) {          mLastScanError = PackageManager.INSTALL_SUCCEEDED; +        String scanPath = scanFile.getPath();          parseFlags |= mDefParseFlags; -        PackageParser pp = new PackageParser(scanFile.getPath()); +        PackageParser pp = new PackageParser(scanPath);          pp.setSeparateProcesses(mSeparateProcesses);          final PackageParser.Package pkg = pp.parsePackage(scanFile, -                scanFile.getPath(), +                scanPath,                  mMetrics, parseFlags);          if (pkg == null) {              mLastScanError = pp.getParseError(); @@ -2066,7 +2153,8 @@ class PackageManagerService extends IPackageManager.Stub {                          // Just remove the loaded entries from package lists.                          mPackages.remove(ps.name);                      } -                    deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString); +                    InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString); +                    args.cleanUpResourcesLI();                      mSettings.enableSystemPackageLP(ps.name);                  }              } @@ -2077,6 +2165,18 @@ class PackageManagerService extends IPackageManager.Stub {          if (ps != null && !ps.codePath.equals(ps.resourcePath)) {              parseFlags |= PackageParser.PARSE_FORWARD_LOCK;          } + +        if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) { +            if (ps != null && ps.resourcePathString != null) { +                pkg.applicationInfo.publicSourceDir = ps.resourcePathString; +            } else { +                // Should not happen at all. Just log an error. +                Log.e(TAG, "Resource path not set for pkg : " + pkg.packageName); +            } +        } else { +            pkg.applicationInfo.publicSourceDir = pkg.mScanPath; +        } +        pkg.applicationInfo.sourceDir = pkg.mScanPath;          // Note that we invoke the following method only if we are about to unpack an application          return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);      } @@ -2230,41 +2330,8 @@ class PackageManagerService extends IPackageManager.Stub {          }          // Initialize package source and resource directories -        File destResourceFile = null; -        File destCodeFile = null; -        if ((scanMode & SCAN_NO_PATHS) == 0) { -            boolean fwdLocked = (parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0; -            final String pkgFileName = pkgName + ".apk"; -            File destDir = null; - -            if (fwdLocked) { -                destDir = mDrmAppPrivateInstallDir; -                destResourceFile = new File(mAppInstallDir, pkgName + ".zip"); -            } else { -                boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0; -                if (!onSd) { -                    destDir = mAppInstallDir; -                } else { -                    String cachePath = getSdDir(pkgName); -                    if (cachePath == null) { -                        Log.e(TAG, "Secure container path for pkg: " + pkgName + " at location: " + cachePath + -                                " not found"); -                        mLastScanError = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; -                        return null; -                    } -                    destDir = new File(cachePath); -                } -                destResourceFile = new File(destDir, pkgFileName); -            } -            destCodeFile = new File(destDir, pkgFileName); -            pkg.mPath = destCodeFile.getAbsolutePath(); -        } else { -            pkg.mPath = pkg.mScanPath; -            destCodeFile = new File(pkg.mScanPath); -            destResourceFile = new File(pkg.mScanPath); -        } -        pkg.applicationInfo.sourceDir = destCodeFile.getAbsolutePath(); -        pkg.applicationInfo.publicSourceDir = destResourceFile.getAbsolutePath(); +        File destCodeFile = new File(pkg.applicationInfo.sourceDir); +        File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);          SharedUserSetting suid = null;          PackageSetting pkgSetting = null; @@ -2568,21 +2635,17 @@ class PackageManagerService extends IPackageManager.Stub {          // We don't expect installation to fail beyond this point,          if ((scanMode&SCAN_MONITOR) != 0) { -            pkg.mPath = destCodeFile.getAbsolutePath();              mAppDirs.put(pkg.mPath, pkg);          }          // Request the ActivityManager to kill the process(only for existing packages)          // so that we do not end up in a confused state while the user is still using the older          // version of the application while the new one gets installed. -        IActivityManager am = ActivityManagerNative.getDefault(); -        if ((am != null) && ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING ) != 0)) { -            try { -                am.killApplicationWithUid(pkg.applicationInfo.packageName, +        if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING ) != 0) { +            killApplication(pkg.applicationInfo.packageName,                          pkg.applicationInfo.uid); -            } catch (RemoteException e) { -            }          } +          synchronized (mPackages) {              // Add the new setting to mSettings              mSettings.insertPackageSettingLP(pkgSetting, pkg); @@ -2835,6 +2898,19 @@ class PackageManagerService extends IPackageManager.Stub {          return pkg;      } +    private void killApplication(String pkgName, int uid) { +        // Request the ActivityManager to kill the process(only for existing packages) +        // so that we do not end up in a confused state while the user is still using the older +        // version of the application while the new one gets installed. +        IActivityManager am = ActivityManagerNative.getDefault(); +        if (am != null) { +            try { +                am.killApplicationWithUid(pkgName, uid); +            } catch (RemoteException e) { +            } +        } +    } +      // The following constants are returned by cachePackageSharedLibsForAbiLI      // to indicate if native shared libraries were found in the package.      // Values are: @@ -3866,26 +3942,51 @@ class PackageManagerService extends IPackageManager.Stub {          mContext.enforceCallingOrSelfPermission(                  android.Manifest.permission.INSTALL_PACKAGES, null); +        Message msg = mHandler.obtainMessage(INIT_COPY); +        msg.obj = createInstallArgs(packageURI, observer, flags, installerPackageName); +        mHandler.sendMessage(msg); +    } + +    private InstallArgs createInstallArgs(Uri packageURI, IPackageInstallObserver observer, +            int flags, String installerPackageName) { +        if (installOnSd(flags)) { +            return new SdInstallArgs(packageURI, observer, flags, +                    installerPackageName); +        } else { +            return new FileInstallArgs(packageURI, observer, flags, +                    installerPackageName); +        } +    } + +    private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) { +        if (installOnSd(flags)) { +            return new SdInstallArgs(fullCodePath, fullResourcePath); +        } else { +            return new FileInstallArgs(fullCodePath, fullResourcePath); +        } +    } + +    private void processPendingInstall(final InstallArgs args, final int currentStatus) {          // Queue up an async operation since the package installation may take a little while.          mHandler.post(new Runnable() {              public void run() {                  mHandler.removeCallbacks(this);                   // Result object to be returned                  PackageInstalledInfo res = new PackageInstalledInfo(); -                res.returnCode = PackageManager.INSTALL_SUCCEEDED; +                res.returnCode = currentStatus;                  res.uid = -1;                  res.pkg = null;                  res.removedInfo = new PackageRemovedInfo(); -                // Make a temporary copy of file from given packageURI -                File tmpPackageFile = copyTempInstallFile(packageURI, res); -                if (tmpPackageFile != null) { +                args.doPreInstall(res.returnCode); +                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {                      synchronized (mInstallLock) { -                        installPackageLI(packageURI, flags, true, installerPackageName, tmpPackageFile, res); +                        installPackageLI(args, true, res);                      } +                    args.doPostInstall(res.returnCode);                  } -                if (observer != null) { +                if (args.observer != null) {                      try { -                        observer.packageInstalled(res.name, res.returnCode); +                        args.observer.packageInstalled(res.name, res.returnCode);                      } catch (RemoteException e) {                          Log.i(TAG, "Observer no longer exists.");                      } @@ -3908,12 +4009,403 @@ class PackageManagerService extends IPackageManager.Stub {                                  res.pkg.applicationInfo.packageName,                                  extras);                      } +                    if (res.removedInfo.args != null) { +                        // Remove the replaced package's older resources safely now +                        synchronized (mInstallLock) { +                            res.removedInfo.args.cleanUpResourcesLI(); +                        } +                    }                  }                  Runtime.getRuntime().gc();              }          });      } +    static abstract class InstallArgs { +        final IPackageInstallObserver observer; +        final int flags; +        final Uri packageURI; +        final String installerPackageName; + +        InstallArgs(Uri packageURI, +                IPackageInstallObserver observer, int flags, +                String installerPackageName) { +            this.packageURI = packageURI; +            this.flags = flags; +            this.observer = observer; +            this.installerPackageName = installerPackageName; +        } + +        abstract void createCopyFile(); +        abstract int copyApk(IMediaContainerService imcs); +        abstract void doPreInstall(int status); +        abstract boolean doRename(int status, String pkgName, String oldCodePath); +        abstract void doPostInstall(int status); +        abstract String getCodePath(); +        abstract String getResourcePath(); +        // Need installer lock especially for dex file removal. +        abstract void cleanUpResourcesLI(); +    } + +    class FileInstallArgs extends InstallArgs { +        File installDir; +        String codeFileName; +        String resourceFileName; + +        FileInstallArgs(Uri packageURI, +                IPackageInstallObserver observer, int flags, +                String installerPackageName) { +            super(packageURI, observer, flags, installerPackageName); +        } + +        FileInstallArgs(String fullCodePath, String fullResourcePath) { +            super(null, null, 0, null); +            File codeFile = new File(fullCodePath); +            installDir = codeFile.getParentFile(); +            codeFileName = fullCodePath; +            resourceFileName = fullResourcePath; +        } + +        void createCopyFile() { +            boolean fwdLocked = isFwdLocked(flags); +            installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir; +            codeFileName = createTempPackageFile(installDir).getPath(); +            resourceFileName = getResourcePathFromCodePath(); +        } + +        String getCodePath() { +            return codeFileName; +        } + +        int copyApk(IMediaContainerService imcs) { +            // Get a ParcelFileDescriptor to write to the output file +            File codeFile = new File(codeFileName); +            ParcelFileDescriptor out = null; +            try { +            out = ParcelFileDescriptor.open(codeFile, +                    ParcelFileDescriptor.MODE_READ_WRITE); +            } catch (FileNotFoundException e) { +                Log.e(TAG, "Failed to create file descritpor for : " + codeFileName); +                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; +            } +            // Copy the resource now +            int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; +            try { +                if (imcs.copyResource(packageURI, out)) { +                    ret = PackageManager.INSTALL_SUCCEEDED; +                } +            } catch (RemoteException e) { +            } finally { +                try { if (out != null) out.close(); } catch (IOException e) {} +            } +            return ret; +        } + +        void doPreInstall(int status) { +            if (status != PackageManager.INSTALL_SUCCEEDED) { +                cleanUp(); +            } +        } + +        boolean doRename(int status, final String pkgName, String oldCodePath) { +            if (status != PackageManager.INSTALL_SUCCEEDED) { +                cleanUp(); +                return false; +            } else { +                // Rename based on packageName +                File codeFile = new File(getCodePath()); +                String apkName = getNextCodePath(oldCodePath, pkgName, ".apk"); +                File desFile = new File(installDir, apkName + ".apk"); +                if (!codeFile.renameTo(desFile)) { +                    return false; +                } +                // Reset paths since the file has been renamed. +                codeFileName = desFile.getPath(); +                resourceFileName = getResourcePathFromCodePath(); +                // Set permissions +                if (!setPermissions(pkgName)) { +                    // Failed setting permissions. +                    return false; +                } +                return true; +            } +        } + +        void doPostInstall(int status) { +            if (status != PackageManager.INSTALL_SUCCEEDED) { +                cleanUp(); +            } +        } + +        String getResourcePath() { +            return resourceFileName; +        } + +        String getResourcePathFromCodePath() { +            String codePath = getCodePath(); +            if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { +                String apkNameOnly = getApkName(codePath); +                return mAppInstallDir.getPath() + "/" + apkNameOnly + ".zip"; +            } else { +                return codePath; +            } +        } + +        private boolean cleanUp() { +            boolean ret = true; +            String sourceDir = getCodePath(); +            String publicSourceDir = getResourcePath(); +            if (sourceDir != null) { +                File sourceFile = new File(sourceDir); +                if (!sourceFile.exists()) { +                    Log.w(TAG, "Package source " + sourceDir + " does not exist."); +                    ret = false; +                } +                // Delete application's code and resources +                sourceFile.delete(); +            } +            if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) { +                final File publicSourceFile = new File(publicSourceDir); +                if (!publicSourceFile.exists()) { +                    Log.w(TAG, "Package public source " + publicSourceFile + " does not exist."); +                } +                if (publicSourceFile.exists()) { +                    publicSourceFile.delete(); +                } +            } +            return ret; +        } + +        void cleanUpResourcesLI() { +            String sourceDir = getCodePath(); +            if (cleanUp() && mInstaller != null) { +                int retCode = mInstaller.rmdex(sourceDir); +                if (retCode < 0) { +                    Log.w(TAG, "Couldn't remove dex file for package: " +                            +  " at location " +                            + sourceDir + ", retcode=" + retCode); +                    // we don't consider this to be a failure of the core package deletion +                } +            } +        } + +        private boolean setPermissions(String pkgName) { +            // TODO Do this in a more elegant way later on. for now just a hack +            if (!isFwdLocked(flags)) { +                final int filePermissions = +                    FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP +                    |FileUtils.S_IROTH; +                int retCode = FileUtils.setPermissions(getCodePath(), filePermissions, -1, -1); +                if (retCode != 0) { +                    Log.e(TAG, "Couldn't set new package file permissions for " + +                            getCodePath() +                            + ". The return code was: " + retCode); +                    // TODO Define new internal error +                    return false; +                } +                return true; +            } +            return true; +        } +    } + +    class SdInstallArgs extends InstallArgs { +        String cid; +        String cachePath; +        static final String RES_FILE_NAME = "pkg.apk"; + +        SdInstallArgs(Uri packageURI, +                IPackageInstallObserver observer, int flags, +                String installerPackageName) { +           super(packageURI, observer, flags, installerPackageName); +        } + +        SdInstallArgs(String fullCodePath, String fullResourcePath) { +            super(null, null, 0, null); +            // Extract cid from fullCodePath +            int eidx = fullCodePath.lastIndexOf("/"); +            String subStr1 = fullCodePath.substring(0, eidx); +            int sidx = subStr1.lastIndexOf("/"); +            cid = subStr1.substring(sidx+1, eidx); +            cachePath = subStr1; +        } + +        void createCopyFile() { +            cid = getTempContainerId(); +        } + +        int copyApk(IMediaContainerService imcs) { +            try { +                cachePath = imcs.copyResourceToContainer( +                        packageURI, cid, +                        getEncryptKey(), RES_FILE_NAME); +            } catch (RemoteException e) { +            } + +            if (cachePath != null) { +                // Mount container once its created with system_uid +                cachePath = mountSdDir(cid, Process.SYSTEM_UID); +            } +            if (cachePath == null) { +                return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; +            } else { +                return PackageManager.INSTALL_SUCCEEDED; +            } +        } + +        @Override +        String getCodePath() { +            return cachePath + "/" + RES_FILE_NAME; +        } + +        @Override +        String getResourcePath() { +            return cachePath + "/" + RES_FILE_NAME; +        } + +        void doPreInstall(int status) { +            if (status != PackageManager.INSTALL_SUCCEEDED) { +                // Destroy container +                destroySdDir(cid); +            } +        } + +        boolean doRename(int status, final String pkgName, +                String oldCodePath) { +            String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME); +            // STOPSHIP TEMPORARY HACK FOR RENAME +            // Create new container at newCachePath +            String codePath = getCodePath(); +            String newCachePath = null; +            final int CREATE_FAILED = 1; +            final int COPY_FAILED = 3; +            final int FINALIZE_FAILED = 5; +            final int PASS = 7; +            int errCode = CREATE_FAILED; +            if ((newCachePath = createSdDir(new File(codePath), newCacheId)) != null) { +                errCode = COPY_FAILED; +                // Copy file from codePath +                if (FileUtils.copyFile(new File(codePath), new File(newCachePath, RES_FILE_NAME))) { +                    errCode = FINALIZE_FAILED; +                    if (finalizeSdDir(newCacheId)) { +                        errCode = PASS; +                    } +                } +            } +            // Print error based on errCode +            String errMsg = ""; +            switch (errCode) { +                case CREATE_FAILED: +                    errMsg = "CREATE_FAILED"; +                    break; +                case COPY_FAILED: +                    errMsg = "COPY_FAILED"; +                    destroySdDir(newCacheId); +                    break; +                case FINALIZE_FAILED: +                    errMsg = "FINALIZE_FAILED"; +                    destroySdDir(newCacheId); +                    break; +                default: +                    errMsg = "PASS"; +                break; +            } +            // Destroy the temporary container +            destroySdDir(cid); +            Log.i(TAG, "Status: " + errMsg); +            if (errCode != PASS) { +                return false; +            } +            cid = newCacheId; +            cachePath = newCachePath; + +            return true; +        } + +        void doPostInstall(int status) { +            if (status != PackageManager.INSTALL_SUCCEEDED) { +                cleanUp(); +            } else { +                // Unmount container +                // Rename and remount based on package name and new uid +            } +        } + +        private void cleanUp() { +            // Destroy secure container +            destroySdDir(cid); +        } + +        void cleanUpResourcesLI() { +            String sourceFile = getCodePath(); +            // Remove dex file +            if (mInstaller != null) { +                int retCode = mInstaller.rmdex(sourceFile.toString()); +                if (retCode < 0) { +                    Log.w(TAG, "Couldn't remove dex file for package: " +                            + " at location " +                            + sourceFile.toString() + ", retcode=" + retCode); +                    // we don't consider this to be a failure of the core package deletion +                } +            } +            cleanUp(); +        } +    }; + +    // Utility method used to create code paths based on package name and available index. +    private static String getNextCodePath(String oldCodePath, String prefix, String suffix) { +        String idxStr = ""; +        int idx = 1; +        if (oldCodePath != null) { +            int eidx = -1; +            if (suffix != null) { +                eidx = oldCodePath.indexOf(suffix); +            } +            if (eidx == -1) { +                eidx = oldCodePath.length(); +            } +            int sidx = oldCodePath.indexOf(prefix); +            if (sidx == -1) { +                sidx = 0; +            } +            String subStr = oldCodePath.substring(sidx + prefix.length(), eidx); +            if (subStr != null) { +                if (subStr.startsWith("-")) { +                    subStr = subStr.substring(1); +                } +                try { +                    idx = Integer.parseInt(subStr); +                    if (idx <= 1) { +                        idx++; +                    } else { +                        idx--; +                    } +                } catch(NumberFormatException e) { +                } +            } +        } +        idxStr = "-" + Integer.toString(idx); +        return prefix + idxStr; +    } + +    // Utility method that returns the relative package path with respect +    // to the installation directory. Like say for /data/data/com.test-1.apk +    // string com.test-1 is returned. +    static String getApkName(String codePath) { +        if (codePath == null) { +            return null; +        } +        int sidx = codePath.lastIndexOf("/"); +        int eidx = codePath.lastIndexOf("."); +        if (eidx == -1) { +            eidx = codePath.length(); +        } else if (eidx == 0) { +            Log.w(TAG, " Invalid code path, "+ codePath + " Not a valid apk name"); +            return null; +        } +        return codePath.substring(sidx+1, eidx); +    } +      class PackageInstalledInfo {          String name;          int uid; @@ -3932,7 +4424,6 @@ class PackageManagerService extends IPackageManager.Stub {          // Remember this for later, in case we need to rollback this install          boolean dataDirExists;          String pkgName = pkg.packageName; -        boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;          if (useEncryptedFilesystemForPackage(pkg)) {              dataDirExists = (new File(mSecureAppDataDir, pkgName)).exists(); @@ -3950,14 +4441,6 @@ class PackageManagerService extends IPackageManager.Stub {              }          }          mLastScanError = PackageManager.INSTALL_SUCCEEDED; -        if (onSd) { -            // Create secure container mount point for package -            String cPath = createSdDir(new File(pkg.mScanPath), pkgName); -            if (cPath == null) { -                mLastScanError = res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; -                return; -            } -        }          PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode);          if (newPackage == null) {              Log.w(TAG, "Package couldn't be installed in " + pkg.mPath); @@ -3965,21 +4448,9 @@ class PackageManagerService extends IPackageManager.Stub {                  res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;              }          } else { -            File destPackageFile = new File(pkg.mPath); -            if (destPackageFile.exists()) { -                // It's safe to do this because we know (from the above check) that the file -                // isn't currently used for an installed package. -                destPackageFile.delete(); -            }              updateSettingsLI(newPackage,                      installerPackageName,                      res); -            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { -                // Check if container can be finalized -                if(onSd && !finalizeSdDir(pkgName)) { -                    res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; -                } -            }              // delete the partially installed application. the data directory will have to be              // restored if it was already existing              if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { @@ -3988,15 +4459,11 @@ class PackageManagerService extends IPackageManager.Stub {                  // scanPackageLocked, unless those directories existed before we even tried to                  // install.                  deletePackageLI( -                        pkgName, true, +                        pkgName, false,                          dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,                                  res.removedInfo);              }          } -        if (onSd && res.returnCode != PackageManager.INSTALL_SUCCEEDED) { -            // Destroy cache -            destroySdDir(pkgName); -        }      }      private void replacePackageLI(PackageParser.Package pkg, @@ -4039,7 +4506,7 @@ class PackageManagerService extends IPackageManager.Stub {          parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING;          // First delete the existing package while retaining the data directory -        if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, +        if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,                  res.removedInfo)) {              // If the existing package was'nt successfully deleted              res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; @@ -4063,17 +4530,10 @@ class PackageManagerService extends IPackageManager.Stub {          if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {              // If we deleted an exisiting package, the old source and resource files that we -            // were keeping around in case we needed them (see below) can now be deleted -            final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo; -            final ApplicationInfo installedPackageAppInfo = -                newPackage.applicationInfo; -            deletePackageResourcesLI(pkgName, -                    !deletedPackageAppInfo.sourceDir -                            .equals(installedPackageAppInfo.sourceDir) -                            ? deletedPackageAppInfo.sourceDir : null, -                    !deletedPackageAppInfo.publicSourceDir -                            .equals(installedPackageAppInfo.publicSourceDir) -                            ? deletedPackageAppInfo.publicSourceDir : null); +            // were keeping around in case we needed them (see below) can now be deleted. +            // This info will be set on the res.removedInfo to clean up later on as post +            // install action. +              //update signature on the new package setting              //this should always succeed, since we checked the              //signature earlier. @@ -4100,22 +4560,15 @@ class PackageManagerService extends IPackageManager.Stub {                      Log.e(TAG, "Failed allocating storage when restoring pkg : " + pkgName);                      return;                  } -                File restoreTmpFile = createTempPackageFile(); -                if (restoreTmpFile == null) { -                    Log.e(TAG, "Failed creating temp file when restoring pkg :  " + pkgName); -                    return; -                } -                if (!FileUtils.copyFile(restoreFile, restoreTmpFile)) { -                    Log.e(TAG, "Failed copying temp file when restoring pkg : " + pkgName); -                    return; -                }                  PackageInstalledInfo restoreRes = new PackageInstalledInfo();                  restoreRes.removedInfo = new PackageRemovedInfo(); -                installPackageLI( -                        Uri.fromFile(restoreFile), -                        isForwardLocked(deletedPackage) -                        ? PackageManager.INSTALL_FORWARD_LOCK -                                : 0, false, oldInstallerPackageName, restoreTmpFile, restoreRes); +                // Parse old package +                parseFlags |= ~PackageManager.INSTALL_REPLACE_EXISTING; +                scanPackageLI(restoreFile, parseFlags, scanMode); +                synchronized (mPackages) { +                    grantPermissionsLP(deletedPackage, false); +                    mSettings.writeLP(); +                }                  if (restoreRes.returnCode != PackageManager.INSTALL_SUCCEEDED) {                      Log.e(TAG, "Failed restoring pkg : " + pkgName + " after failed upgrade");                  } @@ -4220,24 +4673,11 @@ class PackageManagerService extends IPackageManager.Stub {                  return;              }          } -        // XXX There are probably some big issues here: upon doing -        // the rename, we have reached the point of no return (the -        // original .apk is gone!), so we can't fail.  Yet... we can. -        File scanFile = new File(newPackage.mScanPath); -        if (!scanFile.renameTo(new File(newPackage.mPath))) { -            Log.e(TAG, "Couldn't move package file: " + newPackage.mScanPath + " to: " + newPackage.mPath); -            // TODO rename should work. Workaround -            if (!FileUtils.copyFile(scanFile, new File(newPackage.mPath))) { -                Log.e(TAG, "Couldn't move package file to: " + newPackage.mPath); -                res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -            } +        res.returnCode = setPermissionsLI(newPackage); +        if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { +            return;          } else { -            res.returnCode = setPermissionsLI(newPackage); -            if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { -                return; -            } else { -                Log.d(TAG, "New package installed in " + newPackage.mPath); -            } +            Log.d(TAG, "New package installed in " + newPackage.mPath);          }          if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {              if (mInstaller != null) { @@ -4258,67 +4698,11 @@ class PackageManagerService extends IPackageManager.Stub {          }      } -    private File copyTempInstallFile(Uri pPackageURI, -            PackageInstalledInfo res) { -        File tmpPackageFile = createTempPackageFile(); -        int retCode = PackageManager.INSTALL_SUCCEEDED; -        if (tmpPackageFile == null) { -            res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -            return null; -        } - -        if (pPackageURI.getScheme().equals("file")) { -            final File srcPackageFile = new File(pPackageURI.getPath()); -            // We copy the source package file to a temp file and then rename it to the -            // destination file in order to eliminate a window where the package directory -            // scanner notices the new package file but it's not completely copied yet. -            if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { -                Log.e(TAG, "Couldn't copy package file to temp file."); -                retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -            } -        } else if (pPackageURI.getScheme().equals("content")) { -            ParcelFileDescriptor fd = null; -            try { -                fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r"); -            } catch (FileNotFoundException e) { -                Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e); -                retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -            } -            if (fd == null) { -                Log.e(TAG, "Couldn't open file descriptor from download service (null)."); -                retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -            } else { -                if (Config.LOGV) { -                    Log.v(TAG, "Opened file descriptor from download service."); -                } -                ParcelFileDescriptor.AutoCloseInputStream -                        dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); -                // We copy the source package file to a temp file and then rename it to the -                // destination file in order to eliminate a window where the package directory -                // scanner notices the new package file but it's not completely copied yet. -                if (!FileUtils.copyToFile(dlStream, tmpPackageFile)) { -                    Log.e(TAG, "Couldn't copy package stream to temp file."); -                    retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -                } -            } -        } else { -            Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); -            retCode = PackageManager.INSTALL_FAILED_INVALID_URI; -        } - -        res.returnCode = retCode; -        if (retCode != PackageManager.INSTALL_SUCCEEDED) { -            if (tmpPackageFile != null && tmpPackageFile.exists()) { -                tmpPackageFile.delete(); -            } -            return null; -        } -        return tmpPackageFile; -    } - -    private void installPackageLI(Uri pPackageURI, -            int pFlags, boolean newInstall, String installerPackageName, -            File tmpPackageFile, PackageInstalledInfo res) { +    private void installPackageLI(InstallArgs args, +            boolean newInstall, PackageInstalledInfo res) { +        int pFlags = args.flags; +        String installerPackageName = args.installerPackageName; +        File tmpPackageFile = new File(args.getCodePath());          boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);          boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);          boolean replacingExistingPackage = false; @@ -4353,14 +4737,25 @@ class PackageManagerService extends IPackageManager.Stub {                  break main_flow;              } +            // Get rid of all references to package scan path via parser. +            pp = null; +            String oldCodePath = null;              synchronized (mPackages) {                  //check if installing already existing package                  if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0                          && mPackages.containsKey(pkgName)) {                      replacingExistingPackage = true; +                    oldCodePath = mSettings.mPackages.get(pkgName).codePathString;                  }              } +            if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { +                res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; +                break main_flow; +            } +            // TODO rename  pkg.mScanPath In scanPackageLI let it just set values based on mScanPath +            pkg.applicationInfo.sourceDir = pkg.mScanPath= pkg.mPath = args.getCodePath(); +            pkg.applicationInfo.publicSourceDir = args.getResourcePath();              if(replacingExistingPackage) {                  replacePackageLI(pkg, parseFlags, scanMode,                          installerPackageName, res); @@ -4369,15 +4764,16 @@ class PackageManagerService extends IPackageManager.Stub {                          installerPackageName,res);              }          } finally { -            if (tmpPackageFile != null && tmpPackageFile.exists()) { -                tmpPackageFile.delete(); +            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {              }          }      }      private int setPermissionsLI(PackageParser.Package newPackage) {          String pkgName = newPackage.packageName; -        int retCode; +        int retCode = 0; +        // TODO Gross hack but fix later. Ideally move this to be a post installation +        // check after alloting uid.          if ((newPackage.applicationInfo.flags                  & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {              File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir); @@ -4391,7 +4787,7 @@ class PackageManagerService extends IPackageManager.Stub {                  //TODO clean up the extracted public files              }              if (mInstaller != null) { -                retCode = mInstaller.setForwardLockPerm(pkgName, +                retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath),                          newPackage.applicationInfo.uid);              } else {                  final int filePermissions = @@ -4400,15 +4796,16 @@ class PackageManagerService extends IPackageManager.Stub {                                                     newPackage.applicationInfo.uid);              }          } else { -            final int filePermissions = -                    FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP -                    |FileUtils.S_IROTH; -            retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, -1); +            // The permissions on the resource file was set when it was copied for +            // non forward locked apps and apps on sdcard          } +          if (retCode != 0) {              Log.e(TAG, "Couldn't set new package file permissions for " +                      newPackage.mPath                         + ". The return code was: " + retCode); +            // TODO Define new internal error +            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;          }          return PackageManager.INSTALL_SUCCEEDED;      } @@ -4491,10 +4888,10 @@ class PackageManagerService extends IPackageManager.Stub {          }      } -    private File createTempPackageFile() { +    private File createTempPackageFile(File installDir) {          File tmpPackageFile;          try { -            tmpPackageFile = File.createTempFile("vmdl", ".tmp", mAppInstallDir); +            tmpPackageFile = File.createTempFile("vmdl", ".tmp", installDir);          } catch (IOException e) {              Log.e(TAG, "Couldn't create temp file for downloaded package file.");              return null; @@ -4570,6 +4967,13 @@ class PackageManagerService extends IPackageManager.Stub {                  sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras);              }          } +        // Delete the resources here after sending the broadcast to let +        // other processes clean up before deleting resources. +        synchronized (mInstallLock) { +            if (info.args != null) { +                info.args.cleanUpResourcesLI(); +            } +        }          return res;      } @@ -4578,6 +4982,8 @@ class PackageManagerService extends IPackageManager.Stub {          int uid = -1;          int removedUid = -1;          boolean isRemovedPackageSystemUpdate = false; +        // Clean up resources deleted packages. +        InstallArgs args = null;          void sendBroadcast(boolean fullRemove, boolean replacing) {              Bundle extras = new Bundle(1); @@ -4707,36 +5113,6 @@ class PackageManagerService extends IPackageManager.Stub {          return true;      } -    private void deletePackageResourcesLI(String packageName, -            String sourceDir, String publicSourceDir) { -        if (sourceDir != null) { -            File sourceFile = new File(sourceDir); -            if (!sourceFile.exists()) { -                Log.w(TAG, "Package source " + sourceDir + " does not exist."); -            } -            // Delete application's code and resources -            sourceFile.delete(); -            if (mInstaller != null) { -                int retCode = mInstaller.rmdex(sourceFile.toString()); -                if (retCode < 0) { -                    Log.w(TAG, "Couldn't remove dex file for package: " -                            + packageName + " at location " -                            + sourceFile.toString() + ", retcode=" + retCode); -                    // we don't consider this to be a failure of the core package deletion -                } -            } -        } -        if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) { -            final File publicSourceFile = new File(publicSourceDir); -            if (!publicSourceFile.exists()) { -                Log.w(TAG, "Package public source " + publicSourceFile + " does not exist."); -            } -            if (publicSourceFile.exists()) { -                publicSourceFile.delete(); -            } -        } -    } -      private boolean deleteInstalledPackageLI(PackageParser.Package p,              boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) {          ApplicationInfo applicationInfo = p.applicationInfo; @@ -4753,7 +5129,12 @@ class PackageManagerService extends IPackageManager.Stub {          // Delete application code and resources          if (deleteCodeAndResources) { -            deletePackageResourcesLI(applicationInfo.packageName, +            // TODO can pick up from PackageSettings as well +            int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD)!=0) ? +                    PackageManager.INSTALL_ON_SDCARD : 0; +            installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ? +                    PackageManager.INSTALL_FORWARD_LOCK : 0; +            outInfo.args = createInstallArgs(installFlags,                      applicationInfo.sourceDir, applicationInfo.publicSourceDir);          }          return true; @@ -4798,12 +5179,6 @@ class PackageManagerService extends IPackageManager.Stub {              Log.w(TAG, "Package " + p.packageName + " has no applicationInfo.");              return false;          } -        boolean onSd = (p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0; -        // Mount sd container if needed -        if (onSd) { -            // TODO Better error handling from MountService api later -            mountSdDir(p.packageName, Process.SYSTEM_UID) ; -        }          boolean ret = false;          if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {              Log.i(TAG, "Removing system package:"+p.packageName); @@ -4812,22 +5187,10 @@ class PackageManagerService extends IPackageManager.Stub {              ret = deleteSystemPackageLI(p, flags, outInfo);          } else {              Log.i(TAG, "Removing non-system package:"+p.packageName); +            // Kill application pre-emptively especially for apps on sd. +            killApplication(packageName, p.applicationInfo.uid);              ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);          } -        if (ret && onSd) { -            if (deleteCodeAndResources) { -                // Post a delayed destroy on the container since there might -                // be active processes holding open file handles to package -                // resources which will get killed by the process killer when -                // destroying the container. This might even kill the current -                // process and crash the system. Delay the destroy a bit so -                // that the active processes get to handle the uninstall broadcasts. -                sendDelayedDestroySdDir(packageName); -            } else { -                // Just unmount the directory -                unMountSdDir(packageName); -            } -        }          return ret;      } @@ -7520,11 +7883,31 @@ class PackageManagerService extends IPackageManager.Stub {      final private String mSdEncryptKey = "AppsOnSD";      final private String mSdEncryptAlg = "AES";      private boolean mMediaMounted = false; +    private static final int MAX_CONTAINERS = 250; -    private MountService getMountService() { + +    static MountService getMountService() {          return (MountService) ServiceManager.getService("mount");      } +    private String getEncryptKey() { +        try { +            String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey); +            if (sdEncKey == null) { +                sdEncKey = SystemKeyStore.getInstance(). +                        generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey); +                if (sdEncKey == null) { +                    Log.e(TAG, "Failed to create encryption keys"); +                    return null; +                } +            } +            return sdEncKey; +        } catch (NoSuchAlgorithmException nsae) { +            Log.e(TAG, "Failed to create encryption keys with exception: " + nsae); +            return null; +        } +    } +     private String createSdDir(File tmpPackageFile, String pkgName) {          // Create mount point via MountService          MountService mountService = getMountService(); @@ -7535,8 +7918,6 @@ class PackageManagerService extends IPackageManager.Stub {          }          if (DEBUG_SD_INSTALL) Log.i(TAG, "mbLen="+mbLen);          String cachePath = null; -        // Remove any pending destroy messages -        mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);          String sdEncKey;          try {              sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey); @@ -7597,7 +7978,13 @@ class PackageManagerService extends IPackageManager.Stub {     private boolean unMountSdDir(String pkgName) {         // STOPSHIP unmount directory -       return true; +       try { +           getMountService().unmountSecureContainer(pkgName); +           return true; +       } catch (IllegalStateException e) { +           Log.e(TAG, "Failed to unmount : " + pkgName + " with exception " + e); +       } +       return false;     }     private String getSdDir(String pkgName) { @@ -7622,10 +8009,6 @@ class PackageManagerService extends IPackageManager.Stub {     private boolean destroySdDir(String pkgName) {         try { -           if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) { -               // Don't have to send message again -               mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName); -           }             // We need to destroy right away             getMountService().destroySecureContainer(pkgName);             return true; @@ -7635,7 +8018,7 @@ class PackageManagerService extends IPackageManager.Stub {         }     } -   private String[] getSecureContainerList() { +   static String[] getSecureContainerList() {         try {             return getMountService().getSecureContainerList();         } catch (IllegalStateException e) { @@ -7644,13 +8027,47 @@ class PackageManagerService extends IPackageManager.Stub {         return null;     } -   private void sendDelayedDestroySdDir(String pkgName) { -       if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) { -           // Don't have to send message again -           return; +   static String getTempContainerId() { +       String prefix = "smdl1tmp"; +       int tmpIdx = 1; +       String list[] = getSecureContainerList(); +       if (list != null) { +           int idx = 0; +           int idList[] = new int[MAX_CONTAINERS]; +           boolean neverFound = true; +           for (String name : list) { +               // Ignore null entries +               if (name == null) { +                   continue; +               } +               int sidx = name.indexOf(prefix); +               if (sidx == -1) { +                   // Not a temp file. just ignore +                   continue; +               } +               String subStr = name.substring(sidx + prefix.length()); +               idList[idx] = -1; +               if (subStr != null) { +                   try { +                       int cid = Integer.parseInt(subStr); +                       idList[idx++] = cid; +                       neverFound = false; +                   } catch (NumberFormatException e) { +                   } +               } +           } +           if (!neverFound) { +               // Sort idList +               Arrays.sort(idList); +               for (int j = 1; j <= idList.length; j++) { +                   if (idList[j-1] != j) { +                       tmpIdx = j; +                       break; +                   } +               } +           }         } -       Message msg = mHandler.obtainMessage(DESTROY_SD_CONTAINER, pkgName); -       mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY); +       return prefix + tmpIdx;     }     public void updateExternalMediaStatus(final boolean mediaStatus) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 44b662451bc9..c2c6e76e7d99 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -61,6 +61,7 @@ import android.content.pm.ConfigurationInfo;  import android.content.pm.IPackageDataObserver;  import android.content.pm.IPackageManager;  import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo;  import android.content.pm.PackageManager;  import android.content.pm.PathPermission;  import android.content.pm.ProviderInfo; @@ -8845,7 +8846,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen                  sb.append("Process: system_server\n");              } else {                  sb.append("Process: ").append(process.processName).append("\n"); -                sb.append("Flags: 0x").append(Integer.toString(process.info.flags, 16)).append("\n"); +            } +            if (process != null) { +                int flags = process.info.flags; +                IPackageManager pm = ActivityThread.getPackageManager(); +                sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); +                for (String pkg : process.pkgList) { +                    sb.append("Package: ").append(pkg); +                    try { +                        PackageInfo pi = pm.getPackageInfo(pkg, 0); +                        if (pi != null) { +                            sb.append(" v").append(pi.versionCode); +                            if (pi.versionName != null) { +                                sb.append(" (").append(pi.versionName).append(")"); +                            } +                        } +                    } catch (RemoteException e) { +                        Log.e(TAG, "Error getting package info: " + pkg, e); +                    } +                    sb.append("\n"); +                }              }              if (activity != null) {                  sb.append("Activity: ").append(activity.shortComponentName).append("\n"); diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java deleted file mode 100644 index 9e1af3134343..000000000000 --- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.telephonytest; - -import junit.framework.TestSuite; - -import android.test.InstrumentationTestRunner; -import android.test.InstrumentationTestSuite; - -/** - * Instrumentation Test Runner for all Telephony unit tests. - * - * Running all tests: - * - *   runtest telephony-unit - * or - *   adb shell am instrument -w com.android.telephonytest/.TelephonyUnitTestRunner - */ - -public class TelephonyUnitTestRunner extends InstrumentationTestRunner { - -    @Override -    public TestSuite getAllTests() { -        TestSuite suite = new InstrumentationTestSuite(this); -        suite.addTestSuite(com.android.telephonytest.unit.CallerInfoUnitTest.class); -        suite.addTestSuite(com.android.telephonytest.unit.PhoneNumberUtilsUnitTest.class); -        return suite; -    } - -    @Override -    public ClassLoader getLoader() { -        return TelephonyUnitTestRunner.class.getClassLoader(); -    } -} diff --git a/telephony/tests/TelephonyTest/Android.mk b/telephony/tests/telephonytests/Android.mk index 1ef8448d390c..45e265a01e50 100644 --- a/telephony/tests/TelephonyTest/Android.mk +++ b/telephony/tests/telephonytests/Android.mk @@ -7,6 +7,6 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)  LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_PACKAGE_NAME := telephonytest +LOCAL_PACKAGE_NAME := FrameworksTelephonyTests  include $(BUILD_PACKAGE) diff --git a/telephony/tests/TelephonyTest/AndroidManifest.xml b/telephony/tests/telephonytests/AndroidManifest.xml index b2a481bee5d3..70aeebc59d15 100644 --- a/telephony/tests/TelephonyTest/AndroidManifest.xml +++ b/telephony/tests/telephonytests/AndroidManifest.xml @@ -16,7 +16,7 @@  -->  <manifest xmlns:android="http://schemas.android.com/apk/res/android" -    package="com.android.telephonytest"> +    package="com.android.frameworks.telephonytests">      <application>          <uses-library android:name="android.test.runner" /> @@ -28,9 +28,9 @@              </intent-filter>          </activity>      </application> -    <instrumentation android:name=".TelephonyUnitTestRunner" -        android:targetPackage="com.android.telephonytest" -        android:label="Telephony unit tests InstrumentationRunner"> +    <instrumentation android:name="android.test.InstrumentationTestRunner" +        android:targetPackage="com.android.frameworks.telephonytests" +        android:label="Frameworks Telephony Tests">      </instrumentation>      <uses-permission android:name="android.permission.READ_PHONE_STATE" />  </manifest> diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java index aa2981be1390..aa2981be1390 100644 --- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberWatcherTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java index 88eaecd5e5ca..88eaecd5e5ca 100644 --- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberWatcherTest.java +++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/ATResponseParserTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/ATResponseParserTest.java index 81727e482961..81727e482961 100644 --- a/tests/CoreTests/com/android/internal/telephony/ATResponseParserTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/ATResponseParserTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/AdnRecordTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java index 8a4a285212b1..8a4a285212b1 100644 --- a/tests/CoreTests/com/android/internal/telephony/AdnRecordTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java index 3a9c5116f160..3a9c5116f160 100644 --- a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java index 8a66614af0bd..8a66614af0bd 100644 --- a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/SimPhoneBookTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java index 609e76879033..609e76879033 100644 --- a/tests/CoreTests/com/android/internal/telephony/SimPhoneBookTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/SimSmsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java index 1609680c0697..1609680c0697 100644 --- a/tests/CoreTests/com/android/internal/telephony/SimSmsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java index db38ede5f98a..db38ede5f98a 100644 --- a/tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java index bf0c88b7cc2d..bf0c88b7cc2d 100644 --- a/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/TestPhoneNotifier.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java index 427795b0a9ad..427795b0a9ad 100644 --- a/tests/CoreTests/com/android/internal/telephony/TestPhoneNotifier.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java diff --git a/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java index b96743a2fbc3..b96743a2fbc3 100644 --- a/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java diff --git a/tests/CoreTests/com/android/internal/telephony/gsm/GSMTestHandler.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java index fb8a5d9eb936..fb8a5d9eb936 100644 --- a/tests/CoreTests/com/android/internal/telephony/gsm/GSMTestHandler.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java b/telephony/tests/telephonytests/src/com/android/telephonytest/unit/CallerInfoUnitTest.java index 0f24f155046a..0f24f155046a 100644 --- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java +++ b/telephony/tests/telephonytests/src/com/android/telephonytest/unit/CallerInfoUnitTest.java diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java b/telephony/tests/telephonytests/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java index 2d3c548d02a4..2d3c548d02a4 100644 --- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java +++ b/telephony/tests/telephonytests/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java index 69d55c1db232..d7dd601adc86 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java @@ -17,14 +17,17 @@  package com.android.unit_tests;  import android.content.ContentValues; +import android.content.Context;  import android.database.ContentObserver;  import android.database.Cursor;  import android.database.DatabaseUtils;  import android.database.CharArrayBuffer;  import android.database.sqlite.SQLiteDatabase;  import android.database.sqlite.SQLiteStatement; +import android.os.Environment;  import android.os.Handler;  import android.os.Parcel; +import android.test.AndroidTestCase;  import android.test.PerformanceTestCase;  import android.test.suitebuilder.annotation.MediumTest;  import android.test.suitebuilder.annotation.SmallTest; @@ -41,7 +44,7 @@ import junit.framework.TestCase;  import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_COLUMNNAME_INDEX;  import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_DEFAULT_INDEX; -public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase { +public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceTestCase {      private static final String sString1 = "this is a test";      private static final String sString2 = "and yet another test"; @@ -55,7 +58,8 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase      @Override      protected void setUp() throws Exception {          super.setUp(); -        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db"); +        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE); +        mDatabaseFile = new File(dbDir, "database_test.db");          if (mDatabaseFile.exists()) {              mDatabaseFile.delete();          } diff --git a/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java b/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java new file mode 100644 index 000000000000..8e1ff0b8be01 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests.internal.util; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Locale; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import com.android.internal.util.HanziToPinyin; +import com.android.internal.util.HanziToPinyin.Token; + +import junit.framework.TestCase; + +public class HanziToPinyinTest extends TestCase { +    private final static String ONE_HANZI = "\u675C"; +    private final static String TWO_HANZI = "\u675C\u9D51"; +    private final static String ASSIC = "test"; +    private final static String ONE_UNKNOWN = "\uFF71"; +    private final static String MISC = "test\u675C   Test with space\uFF71\uFF71\u675C"; + +    @SmallTest +    public void testGetToken() throws Exception { +        ArrayList<Token> tokens = HanziToPinyin.getInstance().get(ONE_HANZI); +        assertEquals(tokens.size(), 1); +        assertEquals(tokens.get(0).type, Token.PINYIN); +        assertTrue(tokens.get(0).target.equalsIgnoreCase("DU")); + +        tokens = HanziToPinyin.getInstance().get(TWO_HANZI); +        assertEquals(tokens.size(), 2); +        assertEquals(tokens.get(0).type, Token.PINYIN); +        assertEquals(tokens.get(1).type, Token.PINYIN); +        assertTrue(tokens.get(0).target.equalsIgnoreCase("DU")); +        assertTrue(tokens.get(1).target.equalsIgnoreCase("JUAN")); + +        tokens = HanziToPinyin.getInstance().get(ASSIC); +        assertEquals(tokens.size(), 1); +        assertEquals(tokens.get(0).type, Token.LATIN); + +        tokens = HanziToPinyin.getInstance().get(ONE_UNKNOWN); +        assertEquals(tokens.size(), 1); +        assertEquals(tokens.get(0).type, Token.UNKNOWN); + +        tokens = HanziToPinyin.getInstance().get(MISC); +        assertEquals(tokens.size(), 7); +        assertEquals(tokens.get(0).type, Token.LATIN); +        assertEquals(tokens.get(1).type, Token.PINYIN); +        assertEquals(tokens.get(2).type, Token.LATIN); +        assertEquals(tokens.get(3).type, Token.LATIN); +        assertEquals(tokens.get(4).type, Token.LATIN); +        assertEquals(tokens.get(5).type, Token.UNKNOWN); +        assertEquals(tokens.get(6).type, Token.PINYIN); +    } +} diff --git a/tests/CoreTests/android/core/CoreTests.java b/tests/CoreTests/android/core/CoreTests.java deleted file mode 100644 index 442fe0f943ad..000000000000 --- a/tests/CoreTests/android/core/CoreTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.core; - -import com.android.internal.telephony.TelephonyTests; - -import junit.framework.TestSuite; - -import android.graphics.ColorStateListTest; -import android.location.LocationManagerProximityTest; -import android.location.LocationTest; -import android.test.AndroidTestRunnerTest; -import android.test.InstrumentationTestRunnerTest; -import android.util.*; -import android.view.FocusFinderTest; -import android.view.ViewGroupAttributesTest; -import android.webkit.*; - -public class CoreTests extends TestSuite { -     -    /** -     * To run these tests: -     * $ mmm java/tests && adb sync -     * $ adb shell am instrument -w \ -     *    -e class android.core.CoreTests \ -     *    android.core/android.test.InstrumentationTestRunner -     */ -    public static TestSuite suite() { -        TestSuite suite = new TestSuite(CoreTests.class.getName()); - -        // Re-enable StateListDrawableTest when we are running in the -        // framework-test directory which allows access to package private -        // access for MockView -        // suite.addTestSuite(StateListDrawableTest.class); -        suite.addTestSuite(DayOfMonthCursorTest.class); -        suite.addTestSuite(MonthDisplayHelperTest.class); -        suite.addTestSuite(StateSetTest.class); -        suite.addTestSuite(ColorStateListTest.class); -        suite.addTestSuite(FocusFinderTest.class); -        suite.addTestSuite(ViewGroupAttributesTest.class); -        suite.addTest(TelephonyTests.suite()); -        suite.addTestSuite(FloatMathTest.class); -        suite.addTest(JavaTests.suite()); -        suite.addTestSuite(LocationTest.class); -        suite.addTestSuite(LocationManagerProximityTest.class); -        suite.addTestSuite(AndroidTestRunnerTest.class); -        suite.addTestSuite(InstrumentationTestRunnerTest.class); -         -        return suite; -    } -} diff --git a/tests/CoreTests/android/core/JavaTests.java b/tests/CoreTests/android/core/JavaTests.java deleted file mode 100644 index bd8cbf0934a3..000000000000 --- a/tests/CoreTests/android/core/JavaTests.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.core; - -import junit.framework.TestSuite; - -public class JavaTests { -    public static TestSuite suite() { -        TestSuite suite = new TestSuite(JavaTests.class.getName()); - -        //Disabling until bug http://b/issue?id=1200337 is resolved -        //suite.addTestSuite(RequestAPITest.class); -        suite.addTestSuite(MathTest.class); -        suite.addTestSuite(StrictMathTest.class); -        suite.addTestSuite(HashMapPerfTest.class); -        suite.addTestSuite(TreeMapTest.class); -        suite.addTestSuite(FloatDoubleTest.class); -        suite.addTestSuite(Sha1Test.class); -        suite.addTestSuite(NIOTest.class); -        suite.addTestSuite(ReflectArrayTest.class); -        //Commenting out until we find a better way to exclude from continuous testing. -        //suite.addTestSuite(URLTest.class); -        suite.addTestSuite(URITest.class); -        suite.addTestSuite(RegexTest.class); -        suite.addTestSuite(HashMapTest.class); -        suite.addTestSuite(ArrayListTest.class); -        suite.addTestSuite(BooleanTest.class); -        suite.addTestSuite(StringTest.class); -        suite.addTestSuite(BufferedReaderTest.class); -        suite.addTestSuite(CharArrayReaderTest.class); -        suite.addTestSuite(PushbackReaderTest.class); -        suite.addTestSuite(StringReaderTest.class); -        suite.addTestSuite(StreamTokenizerTest.class); -        suite.addTestSuite(ByteArrayInputStreamTest.class); -        suite.addTestSuite(DataInputStreamTest.class); -        suite.addTestSuite(BufferedInputStreamTest.class); -        suite.addTestSuite(PushbackInputStreamTest.class); -        suite.addTestSuite(ByteArrayOutputStreamTest.class); -        suite.addTestSuite(DataOutputStreamTest.class); -        suite.addTestSuite(BufferedOutputStreamTest.class); -        suite.addTestSuite(CharArrayWriterTest.class); -        suite.addTestSuite(StringWriterTest.class); -        suite.addTestSuite(PrintWriterTest.class); -        suite.addTestSuite(BufferedWriterTest.class); -        suite.addTestSuite(ClassTest.class); -        //To be unccommented when Bug #799327 is fixed. -        //suite.addTestSuite(ClassLoaderTest.class); -        suite.addTestSuite(LineNumberReaderTest.class); -        suite.addTestSuite(InputStreamReaderTest.class); -        suite.addTestSuite(OutputStreamWriterTest.class); -        suite.addTestSuite(EnumTest.class); -        suite.addTestSuite(ParseIntTest.class); -        suite.addTestSuite(PipedStreamTest.class); -        suite.addTestSuite(LocaleTest.class); -        //Commenting out until we find a better way to exclude from continuous testing. -        //suite.addTestSuite(InetAddrTest.class); -        suite.addTestSuite(SocketTest.class); -        suite.addTestSuite(ChecksumTest.class); -        suite.addTestSuite(DeflateTest.class); -        suite.addTestSuite(ZipStreamTest.class); -        suite.addTestSuite(GZIPStreamTest.class); -        suite.addTestSuite(ZipFileTest.class); -        suite.addTestSuite(FileTest.class); -        suite.addTestSuite(SQLiteJDBCDriverTest.class); -        suite.addTestSuite(AtParserTest.class); -        suite.addTestSuite(DatagramTest.class); -        suite.addTestSuite(CryptoTest.class); -        suite.addTestSuite(MiscRegressionTest.class); - -        return suite; -    } -} diff --git a/tests/CoreTests/android/location/LocationManagerProximityTest.java b/tests/CoreTests/android/location/LocationManagerProximityTest.java deleted file mode 100644 index e82d8780ddef..000000000000 --- a/tests/CoreTests/android/location/LocationManagerProximityTest.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.location; - -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationManager; -import android.provider.Settings; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.Suppress; -import android.util.Log; - -/** - * Tests for LocationManager.addProximityAlert - *  - * TODO: add tests for more scenarios - *  - * To run: - *  adb shell am instrument -e class android.location.LocationManagerProximityTest \ - *     -w android.core/android.test.InstrumentationTestRunner - *      - * This test requires that the "Allow mock locations" setting be enabled. - * To ensure reliable results, all location providers should be disabled. - *  - */ -@Suppress -@MediumTest -public class LocationManagerProximityTest extends AndroidTestCase { - -    private static final int UPDATE_LOCATION_WAIT_TIME = 1000; -    private static final int PROXIMITY_WAIT_TIME = 2000; - -    private LocationManager mLocationManager; -    private PendingIntent mPendingIntent; -    private TestIntentReceiver mIntentReceiver; - -    private static final String LOG_TAG = "LocationProximityTest"; - -    private static final String PROVIDER_NAME = "test"; - -    @Override -    protected void setUp() throws Exception { -        super.setUp(); - -        // test that mock locations are allowed so a more descriptive error message can be logged -        if (Settings.Secure.getInt(getContext().getContentResolver(), -            Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) { -            fail("Mock locations are currently disabled in Settings - this test requires " + -                 "mock locations"); -        } - -        mLocationManager = (LocationManager) getContext(). -                getSystemService(Context.LOCATION_SERVICE); -        if (mLocationManager.getProvider(PROVIDER_NAME) != null) { -            mLocationManager.removeTestProvider(PROVIDER_NAME); -        } -         -        mLocationManager.addTestProvider(PROVIDER_NAME, true, //requiresNetwork, -                false, // requiresSatellite, -                true, // requiresCell, -                false, // hasMonetaryCost, -                false, // supportsAltitude, -                false, // supportsSpeed, s -                false, // upportsBearing, -                Criteria.POWER_MEDIUM, // powerRequirement -                Criteria.ACCURACY_FINE); // accuracy -        mLocationManager.setTestProviderEnabled(PROVIDER_NAME, true); -    } - -    @Override -    protected void tearDown() throws Exception { -        mLocationManager.removeTestProvider(PROVIDER_NAME); - -        if (mPendingIntent != null) { -            mLocationManager.removeProximityAlert(mPendingIntent); -        } -        if (mIntentReceiver != null) { -            getContext().unregisterReceiver(mIntentReceiver); -        } -    } - -    /** -     * Tests basic proximity alert when entering proximity -     */ -    public void testEnterProximity() throws Exception { -        doTestEnterProximity(10000); -    } - -    /** -     * Tests proximity alert when entering proximity, with no expiration  -     */ -    public void testEnterProximity_noexpire() throws Exception { -        doTestEnterProximity(-1); -    } - -    /** -     * Helper variant for testing enter proximity scenario  -     * TODO: add additional parameters as more scenarios are added -     *  -     * @param expiration - expiry of proximity alert  -     */ -    private void doTestEnterProximity(long expiration) throws Exception { -        // update location to outside proximity range -        synchronousSendLocation(30, 30); -        registerProximityListener(0, 0, 1000, expiration); -        sendLocation(0, 0); -        waitForAlert(); -        assertProximityType(true); -    } - -    /** -     * Tests basic proximity alert when exiting proximity -     */ -    public void testExitProximity() throws Exception { -        // first do enter proximity scenario -        doTestEnterProximity(-1); - -        // now update to trigger exit proximity proximity -        mIntentReceiver.clearReceivedIntents(); -        sendLocation(20, 20); -        waitForAlert(); -        assertProximityType(false); -    } - -    /** -     * Registers the proximity intent receiver -     */ -    private void registerProximityListener(double latitude, double longitude,  -            float radius, long expiration) { -        String intentKey = "testProximity"; -        Intent proximityIntent = new Intent(intentKey); -        mPendingIntent = PendingIntent.getBroadcast(getContext(), 0,  -                proximityIntent, PendingIntent.FLAG_CANCEL_CURRENT); -        mIntentReceiver = new TestIntentReceiver(intentKey); - -        mLocationManager.addProximityAlert(latitude, longitude, radius,  -                expiration, mPendingIntent); - -        getContext().registerReceiver(mIntentReceiver,  -                mIntentReceiver.getFilter()); - -    } - -    /** -     * Blocks until proximity intent notification is received -     * @throws InterruptedException -     */ -    private void waitForAlert() throws InterruptedException { -        Log.d(LOG_TAG, "Waiting for proximity update"); -        synchronized (mIntentReceiver) { -            mIntentReceiver.wait(PROXIMITY_WAIT_TIME); -        } - -        assertNotNull("Did not receive proximity alert",  -                mIntentReceiver.getLastReceivedIntent()); -    } - -    /** -     * Asserts that the received intent had the enter proximity property set as -     * expected -     * @param expectedEnterProximity - true if enter proximity expected, false if -     *   exit expected -     */ -    private void assertProximityType(boolean expectedEnterProximity)  -            throws Exception { -        boolean proximityTest = mIntentReceiver.getLastReceivedIntent(). -                getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING,  -                !expectedEnterProximity); -        assertEquals("proximity alert not set to expected enter proximity value", -                expectedEnterProximity, proximityTest); -    } - -    /** -     * Synchronous variant of sendLocation -     */ -    private void synchronousSendLocation(final double latitude,  -            final double longitude) -            throws InterruptedException { -        sendLocation(latitude, longitude, this); -        // wait for location to be set -        synchronized (this) { -            wait(UPDATE_LOCATION_WAIT_TIME); -        } -    } - -    /** -     * Asynchronously update the mock location provider without notification -     */ -    private void sendLocation(final double latitude, final double longitude) { -        sendLocation(latitude, longitude, null); -    } - -    /** -     * Asynchronously update the mock location provider with given latitude and -     * longitude -     *  -     * @param latitude - update location -     * @param longitude - update location -     * @param observer - optionally, object to notify when update is sent.If -     *            null, no update will be sent -     */ -    private void sendLocation(final double latitude, final double longitude,  -            final Object observer) { -        Thread locationUpdater = new Thread() { -            @Override -            public void run() { -                Location loc = new Location(PROVIDER_NAME); -                loc.setLatitude(latitude); -                loc.setLongitude(longitude); - -                loc.setTime(java.lang.System.currentTimeMillis()); -                Log.d(LOG_TAG, "Sending update for " + PROVIDER_NAME); -                mLocationManager.setTestProviderLocation(PROVIDER_NAME, loc); -                if (observer != null) { -                    synchronized (observer) { -                        observer.notify(); -                    } -                } -            } -        }; -        locationUpdater.start(); - -    } - -    /** -     * Helper class that receives a proximity intent and notifies the main class -     * when received -     */ -    private static class TestIntentReceiver extends BroadcastReceiver { - -        private String mExpectedAction; -        private Intent mLastReceivedIntent; - -        public TestIntentReceiver(String expectedAction) { -            mExpectedAction = expectedAction; -            mLastReceivedIntent = null; -        } - -        public IntentFilter getFilter() { -            IntentFilter filter = new IntentFilter(mExpectedAction); -            return filter; -        } - -        @Override -        public void onReceive(Context context, Intent intent) { -            if (intent != null && mExpectedAction.equals(intent.getAction())) { -                Log.d(LOG_TAG, "Intent Received: " + intent.toString()); -                mLastReceivedIntent = intent; -                synchronized (this) { -                    notify(); -                } -            } -        } - -        public Intent getLastReceivedIntent() { -            return mLastReceivedIntent; -        } -         -        public void clearReceivedIntents() { -            mLastReceivedIntent = null; -        } -    } -} diff --git a/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java b/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java deleted file mode 100644 index fdfafe103008..000000000000 --- a/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -import com.android.internal.telephony.AdnRecordTest; -import com.android.internal.telephony.gsm.GSMPhoneTest; -import com.android.internal.telephony.GsmAlphabetTest; -import com.android.internal.telephony.SMSDispatcherTest; -import com.android.internal.telephony.SimPhoneBookTest; -import com.android.internal.telephony.SimSmsTest; -import com.android.internal.telephony.SimUtilsTest; - -import junit.framework.TestSuite; - -/** - * To run these tests: - * $ mmm java/tests && adb sync - * $ adb shell am instrument -w \ - *    -e class com.android.internal.telephony.TelephonyTests \ - *    android.core/android.test.InstrumentationTestRunner - */ -public class TelephonyTests { -    public static TestSuite suite() { -        TestSuite suite = new TestSuite(TelephonyTests.class.getName()); - -        suite.addTestSuite(PhoneNumberWatcherTest.class); -        suite.addTestSuite(ATResponseParserTest.class); -        suite.addTestSuite(PhoneNumberUtilsTest.class); -        suite.addTestSuite(SMSDispatcherTest.class); -        //suite.addTestSuite(GSMPhoneTest.class); -        suite.addTestSuite(AdnRecordTest.class); -        suite.addTestSuite(GsmAlphabetTest.class); -        suite.addTestSuite(SimUtilsTest.class); -        suite.addTestSuite(SimPhoneBookTest.class); -        suite.addTestSuite(SimSmsTest.class); -        suite.addTestSuite(TelephonyUtilsTest.class); - -        return suite; -    } -}  |