Merge "Media server death nodification"
diff --git a/Android.mk b/Android.mk
index ded8173..d6beac5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,6 +75,7 @@
core/java/android/app/IActivityWatcher.aidl \
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IBackupAgent.aidl \
+ core/java/android/app/IDevicePolicyManager.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/ISearchManager.aidl \
@@ -110,6 +111,7 @@
core/java/android/os/ICheckinService.aidl \
core/java/android/os/IMessenger.aidl \
core/java/android/os/IMountService.aidl \
+ core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/INetStatService.aidl \
core/java/android/os/IParentalControlCallback.aidl \
core/java/android/os/IPermissionController.aidl \
diff --git a/api/current.xml b/api/current.xml
index b690e42..ee7d330 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -177,6 +177,17 @@
visibility="public"
>
</field>
+<field name="BIND_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.permission.BIND_DEVICE_ADMIN""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BIND_INPUT_METHOD"
type="java.lang.String"
transient="false"
@@ -188,6 +199,17 @@
visibility="public"
>
</field>
+<field name="BIND_WALLPAPER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.permission.BIND_WALLPAPER""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BLUETOOTH"
type="java.lang.String"
transient="false"
@@ -19831,6 +19853,600 @@
</parameter>
</method>
</interface>
+<class name="DeviceAdmin"
+ extends="android.content.BroadcastReceiver"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DeviceAdmin"
+ type="android.app.DeviceAdmin"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getManager"
+ return="android.app.DevicePolicyManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getWho"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="onDisabled"
+ return="void"
+ 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="onEnabled"
+ return="void"
+ 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="onPasswordChanged"
+ return="void"
+ 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="onPasswordFailed"
+ return="void"
+ 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="onPasswordSucceeded"
+ return="void"
+ 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="onReceive"
+ return="void"
+ 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>
+<field name="ACTION_DEVICE_ADMIN_DISABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.DEVICE_ADMIN_DISABLED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DEVICE_ADMIN_ENABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.DEVICE_ADMIN_ENABLED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.ACTION_PASSWORD_CHANGED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_FAILED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.ACTION_PASSWORD_FAILED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_SUCCEEDED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.ACTION_PASSWORD_SUCCEEDED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DEVICE_ADMIN_META_DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.device_admin""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DeviceAdminInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="DeviceAdminInfo"
+ type="android.app.DeviceAdminInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="receiver" type="android.content.pm.ResolveInfo">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException">
+</exception>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dump"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pw" type="android.util.Printer">
+</parameter>
+<parameter name="prefix" type="java.lang.String">
+</parameter>
+</method>
+<method name="getActivityInfo"
+ return="android.content.pm.ActivityInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getComponent"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPackageName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getReceiverName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="loadLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DevicePolicyManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getActiveMinimumPasswordLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getActivePasswordMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentFailedPasswordAttempts"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMaximumTimeToLock"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMinimumPasswordLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPasswordMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isAdminActive"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="who" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="removeActiveAdmin"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="who" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="setMaximumTimeToLock"
+ 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="timeMs" type="long">
+</parameter>
+</method>
+<method name="setMinimumPasswordLength"
+ 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="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMode"
+ 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="mode" type="int">
+</parameter>
+</method>
+<method name="wipeData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="ACTION_ADD_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.ADD_DEVICE_ADMIN""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_SET_NEW_PASSWORD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.action.SET_NEW_PASSWORD""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.app.extra.DEVICE_ADMIN""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_ALPHANUMERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_NUMERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_UNSPECIFIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIPE_EXTERNAL_STORAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIPE_LOW_LEVEL_FORMAT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="Dialog"
extends="java.lang.Object"
abstract="false"
@@ -32576,6 +33192,17 @@
visibility="public"
>
</field>
+<field name="DEVICE_POLICY_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""device_policy""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="DROPBOX_SERVICE"
type="java.lang.String"
transient="false"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ad6540a..e65cdf1 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -34,12 +34,14 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <media/mediametadataretriever.h>
using namespace android;
static long gNumRepetitions;
static long gMaxNumFrames; // 0 means decode all available.
static long gReproduceBug; // if not -1.
+static bool gPreferSoftwareCodec;
static int64_t getNowUs() {
struct timeval tv;
@@ -59,7 +61,9 @@
rawSource = source;
} else {
rawSource = OMXCodec::Create(
- client->interface(), meta, false /* createEncoder */, source);
+ client->interface(), meta, false /* createEncoder */, source,
+ NULL /* matchComponentName */,
+ gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0);
if (rawSource == NULL) {
fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
@@ -219,6 +223,8 @@
fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
fprintf(stderr, " -b bug to reproduce\n");
fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n");
+ fprintf(stderr, " -t(humbnail) extract video thumbnail\n");
+ fprintf(stderr, " -s(oftware) prefer software codec\n");
}
int main(int argc, char **argv) {
@@ -227,12 +233,14 @@
bool audioOnly = false;
bool listComponents = false;
bool dumpProfiles = false;
+ bool extractThumbnail = false;
gNumRepetitions = 1;
gMaxNumFrames = 0;
gReproduceBug = -1;
+ gPreferSoftwareCodec = false;
int res;
- while ((res = getopt(argc, argv, "han:lm:b:p")) >= 0) {
+ while ((res = getopt(argc, argv, "han:lm:b:pts")) >= 0) {
switch (res) {
case 'a':
{
@@ -274,6 +282,18 @@
break;
}
+ case 't':
+ {
+ extractThumbnail = true;
+ break;
+ }
+
+ case 's':
+ {
+ gPreferSoftwareCodec = true;
+ break;
+ }
+
case '?':
case 'h':
default:
@@ -288,6 +308,34 @@
argc -= optind;
argv += optind;
+ if (extractThumbnail) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service.get() != NULL);
+
+ sp<IMediaMetadataRetriever> retriever =
+ service->createMetadataRetriever(getpid());
+
+ CHECK(retriever != NULL);
+
+ for (int k = 0; k < argc; ++k) {
+ const char *filename = argv[k];
+
+ CHECK_EQ(retriever->setDataSource(filename), OK);
+ CHECK_EQ(retriever->setMode(METADATA_MODE_FRAME_CAPTURE_ONLY), OK);
+
+ sp<IMemory> mem = retriever->captureFrame();
+
+ printf("captureFrame(%s) => %s\n",
+ filename, mem != NULL ? "OK" : "FAILED");
+ }
+
+ return 0;
+ }
+
if (dumpProfiles) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
@@ -389,7 +437,8 @@
sp<MetaData> meta;
size_t i;
for (i = 0; i < numTracks; ++i) {
- meta = extractor->getTrackMetaData(i);
+ meta = extractor->getTrackMetaData(
+ i, MediaExtractor::kIncludeExtensiveMetaData);
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
@@ -403,6 +452,12 @@
}
}
+ int64_t thumbTimeUs;
+ if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
+ printf("thumbnailTime: %lld us (%.2f secs)\n",
+ thumbTimeUs, thumbTimeUs / 1E6);
+ }
+
mediaSource = extractor->getTrack(i);
}
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index d89b877..fe05393 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -186,6 +186,7 @@
private boolean mRestricted;
private AccountManager mAccountManager; // protected by mSync
private DropBoxManager mDropBoxManager = null;
+ private DevicePolicyManager mDevicePolicyManager = null;
private final Object mSync = new Object();
@@ -895,6 +896,8 @@
return getWallpaperManager();
} else if (DROPBOX_SERVICE.equals(name)) {
return getDropBoxManager();
+ } else if (DEVICE_POLICY_SERVICE.equals(name)) {
+ return getDevicePolicyManager();
}
return null;
@@ -1064,6 +1067,16 @@
return mDropBoxManager;
}
+ private DevicePolicyManager getDevicePolicyManager() {
+ synchronized (mSync) {
+ if (mDevicePolicyManager == null) {
+ mDevicePolicyManager = new DevicePolicyManager(this,
+ mMainThread.getHandler());
+ }
+ }
+ return mDevicePolicyManager;
+ }
+
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
new file mode 100644
index 0000000..4da3fee
--- /dev/null
+++ b/core/java/android/app/DeviceAdmin.java
@@ -0,0 +1,221 @@
+/*
+ * 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.app;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Base class for implementing a device administration component. This
+ * class provides a convenience for interpreting the raw intent actions
+ * that are sent by the system.
+ *
+ * <p>When publishing your DeviceAdmin subclass as a receiver, it must
+ * handle {@link #ACTION_DEVICE_ADMIN_ENABLED} and require the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission. A typical
+ * manifest entry would look like:</p>
+ *
+ * <pre>{@include development/samples/ApiDemos/AndroidManifest.xml
+ * device_admin_declaration}</pre>
+ *
+ * <p>The meta-data referenced here provides addition information specific
+ * to the device administrator, as parsed by the {@link DeviceAdminInfo} class.
+ * A typical file would be:</p>
+ *
+ * <pre>{@include development/samples/ApiDemos/res/xml/sample_device_admin.xml
+ * meta_data}</pre>
+ */
+public class DeviceAdmin extends BroadcastReceiver {
+ private static String TAG = "DevicePolicy";
+ private static boolean DEBUG = false;
+ private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+ /**
+ * This is the primary action that a device administrator must implement to be
+ * allowed to manage a device. This will be set to the receiver
+ * when the user enables it for administration. You will generally
+ * handle this in {@link DeviceAdmin#onEnabled(Context, Intent)}. To be
+ * supported, the receiver must also require the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission so
+ * that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_ADMIN_ENABLED
+ = "android.app.action.DEVICE_ADMIN_ENABLED";
+
+ /**
+ * 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
+ * handle this in {@link DeviceAdmin#onDisabled(Context, Intent)}. Note
+ * that this action will be
+ * sent the receiver regardless of whether it is explicitly listed in
+ * its intent filter.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_ADMIN_DISABLED
+ = "android.app.action.DEVICE_ADMIN_DISABLED";
+
+ /**
+ * Action sent to a device administrator when the user has changed the
+ * password of their device. You can at this point check the characteristics
+ * of the new password with {@link DevicePolicyManager#getActivePasswordMode()
+ * DevicePolicyManager.getActivePasswordMode()} and
+ * {@link DevicePolicyManager#getActiveMinimumPasswordLength()
+ * DevicePolicyManager.getActiveMinimumPasswordLength()}. You will generally
+ * handle this in {@link DeviceAdmin#onPasswordChanged(Context, Intent)}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PASSWORD_CHANGED
+ = "android.app.action.ACTION_PASSWORD_CHANGED";
+
+ /**
+ * Action sent to a device administrator when the user has failed at
+ * attempted to enter the password. You can at this point check the
+ * number of failed password attempts there have been with
+ * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+ * DevicePolicyManager.getCurrentFailedPasswordAttempts()}. You will generally
+ * handle this in {@link DeviceAdmin#onPasswordFailed(Context, Intent)}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PASSWORD_FAILED
+ = "android.app.action.ACTION_PASSWORD_FAILED";
+
+ /**
+ * Action sent to a device administrator when the user has successfully
+ * entered their password, after failing one or more times.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PASSWORD_SUCCEEDED
+ = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
+
+ /**
+ * Name under which an DevicePolicy component publishes information
+ * about itself. This meta-data must reference an XML resource containing
+ * a device-admin tag. XXX TO DO: describe syntax.
+ */
+ public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
+
+ private DevicePolicyManager mManager;
+ private ComponentName mWho;
+
+ /**
+ * Retrieve the DevicePolicyManager interface for this administrator to work
+ * with the system.
+ */
+ public DevicePolicyManager getManager(Context context) {
+ if (mManager != null) {
+ return mManager;
+ }
+ mManager = (DevicePolicyManager)context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ return mManager;
+ }
+
+ /**
+ * Retrieve the ComponentName describing who this device administrator is, for
+ * use in {@link DevicePolicyManager} APIs that require the administrator to
+ * identify itself.
+ */
+ public ComponentName getWho(Context context) {
+ if (mWho != null) {
+ return mWho;
+ }
+ mWho = new ComponentName(context, getClass());
+ return mWho;
+ }
+
+ /**
+ * Called after the administrator is first enabled, as a result of
+ * receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}. At this point you
+ * can use {@link DevicePolicyManager} to set your desired policies.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onEnabled(Context context, Intent intent) {
+ }
+
+ /**
+ * 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}
+ * API.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onDisabled(Context context, Intent intent) {
+ }
+
+ /**
+ * Called after the user has changed their password, as a result of
+ * receiving {@link #ACTION_PASSWORD_CHANGED}. At this point you
+ * can use {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+ * DevicePolicyManager.getCurrentFailedPasswordAttempts()}
+ * to retrieve the active password characteristics.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onPasswordChanged(Context context, Intent intent) {
+ }
+
+ /**
+ * Called after the user has failed at entering their current password, as a result of
+ * receiving {@link #ACTION_PASSWORD_FAILED}. At this point you
+ * can use {@link DevicePolicyManager} to retrieve the number of failed
+ * password attempts.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onPasswordFailed(Context context, Intent intent) {
+ }
+
+ /**
+ * Called after the user has succeeded at entering their current password,
+ * as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}. This will
+ * only be received the first time they succeed after having previously
+ * failed.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onPasswordSucceeded(Context context, Intent intent) {
+ }
+
+ /**
+ * Intercept standard device administrator broadcasts. Implementations
+ * should not override this method; it is better to implement the
+ * convenience callbacks for each action.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_PASSWORD_CHANGED.equals(action)) {
+ onPasswordChanged(context, intent);
+ } else if (ACTION_PASSWORD_FAILED.equals(action)) {
+ onPasswordFailed(context, intent);
+ } else if (ACTION_PASSWORD_SUCCEEDED.equals(action)) {
+ onPasswordSucceeded(context, intent);
+ } else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {
+ onEnabled(context, intent);
+ } 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
new file mode 100644
index 0000000..eac6e46
--- /dev/null
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -0,0 +1,186 @@
+/*
+ * 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.app;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Printer;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a device administrator
+ * component.
+ */
+public final class DeviceAdminInfo implements Parcelable {
+ static final String TAG = "DeviceAdminInfo";
+
+ /**
+ * The BroadcastReceiver that implements this device admin component.
+ */
+ final ResolveInfo mReceiver;
+
+ /**
+ * Constructor.
+ *
+ * @param context The Context in which we are parsing the device admin.
+ * @param receiver The ResolveInfo returned from the package manager about
+ * this device admin's component.
+ */
+ public DeviceAdminInfo(Context context, ResolveInfo receiver)
+ throws XmlPullParserException, IOException {
+ mReceiver = receiver;
+ ActivityInfo ai = receiver.activityInfo;
+
+ PackageManager pm = context.getPackageManager();
+
+ XmlResourceParser parser = null;
+ try {
+ parser = ai.loadXmlMetaData(pm, DeviceAdmin.DEVICE_ADMIN_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No "
+ + DeviceAdmin.DEVICE_ADMIN_META_DATA + " meta-data");
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"device-admin".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with device-admin tag");
+ }
+
+ TypedArray sa = context.getResources().obtainAttributes(attrs,
+ com.android.internal.R.styleable.Wallpaper);
+
+ sa.recycle();
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ DeviceAdminInfo(Parcel source) {
+ mReceiver = ResolveInfo.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Return the .apk package that implements this device admin.
+ */
+ public String getPackageName() {
+ return mReceiver.activityInfo.packageName;
+ }
+
+ /**
+ * Return the class name of the receiver component that implements
+ * this device admin.
+ */
+ public String getReceiverName() {
+ return mReceiver.activityInfo.name;
+ }
+
+ /**
+ * Return the raw information about the receiver implementing this
+ * device admin. Do not modify the returned object.
+ */
+ public ActivityInfo getActivityInfo() {
+ return mReceiver.activityInfo;
+ }
+
+ /**
+ * Return the component of the receiver that implements this device admin.
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mReceiver.activityInfo.packageName,
+ mReceiver.activityInfo.name);
+ }
+
+ /**
+ * Load the user-displayed label for this device admin.
+ *
+ * @param pm Supply a PackageManager used to load the device admin's
+ * resources.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ return mReceiver.loadLabel(pm);
+ }
+
+ /**
+ * Load the user-displayed icon for this device admin.
+ *
+ * @param pm Supply a PackageManager used to load the device admin's
+ * resources.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ return mReceiver.loadIcon(pm);
+ }
+
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "Receiver:");
+ mReceiver.dump(pw, prefix + " ");
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceAdminInfo{" + mReceiver.activityInfo.name + "}";
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ mReceiver.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<DeviceAdminInfo> CREATOR =
+ new Parcelable.Creator<DeviceAdminInfo>() {
+ public DeviceAdminInfo createFromParcel(Parcel source) {
+ return new DeviceAdminInfo(source);
+ }
+
+ public DeviceAdminInfo[] newArray(int size) {
+ return new DeviceAdminInfo[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
new file mode 100644
index 0000000..4fdfe0a
--- /dev/null
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -0,0 +1,425 @@
+/*
+ * 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.app;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Public interface for managing policies enforced on a device. Most clients
+ * of this class must have published a {@link DeviceAdmin} that the user
+ * has currently enabled.
+ */
+public class DevicePolicyManager {
+ private static String TAG = "DevicePolicyManager";
+ private static boolean DEBUG = false;
+ private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final IDevicePolicyManager mService;
+
+ /*package*/ DevicePolicyManager(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mService = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ }
+
+ /**
+ * Activity action: ask the user to add a new device administrator to the system.
+ * The desired policy is the ComponentName of the policy in the
+ * {@link #EXTRA_DEVICE_ADMIN} extra field. This will invoke a UI to
+ * bring the user through adding the device administrator to the system (or
+ * allowing them to reject it).
+ *
+ * <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.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ADD_DEVICE_ADMIN
+ = "android.app.action.ADD_DEVICE_ADMIN";
+
+ /**
+ * The ComponentName of the administrator component.
+ *
+ * @see #ACTION_ADD_DEVICE_ADMIN
+ */
+ public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+
+ /**
+ * 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
+ * user enter a new password that meets the current requirements. If the
+ * current password is sufficient, the activity will exit immediately without
+ * being displayed to the user. Upon receiving a result from this activity,
+ * you can check the new password characteristics to see if they are
+ * sufficient.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SET_NEW_PASSWORD
+ = "android.app.action.SET_NEW_PASSWORD";
+
+ /**
+ * Return true if the given administrator component is currently
+ * active (enabled) in the system.
+ */
+ public boolean isAdminActive(ComponentName who) {
+ if (mService != null) {
+ try {
+ return who.equals(mService.getActiveAdmin());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a current administration component. This can only be called
+ * by the application that owns the administration component; if you
+ * try to remove someone else's component, a security exception will be
+ * thrown.
+ */
+ public void removeActiveAdmin(ComponentName who) {
+ if (mService != null) {
+ try {
+ mService.removeActiveAdmin(who);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Constant for {@link #setPasswordMode}: the policy has no requirements
+ * for the password.
+ */
+ public static final int PASSWORD_MODE_UNSPECIFIED = 0;
+
+ /**
+ * Constant for {@link #setPasswordMode}: the user must have at least a
+ * numeric password.
+ */
+ public static final int PASSWORD_MODE_NUMERIC = 1000;
+
+ /**
+ * Constant for {@link #setPasswordMode}: the user must have at least an
+ * alphanumeric password.
+ */
+ public static final int PASSWORD_MODE_ALPHANUMERIC = 2000;
+
+ /**
+ * Called by an application that is administering the device to set the
+ * password restrictions it is imposing. After setting this, the user
+ * will not be able to enter a new password that is not at least as
+ * restrictive as what has been set. Note that the current password
+ * will remain until the user has set a new one, so the change does not
+ * take place immediately. To prompt the user for a new password, use
+ * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.
+ *
+ * @param admin Which {@link DeviceAdmin} this request is associated with.
+ * @param mode The new desired mode. One of
+ * {@link #PASSWORD_MODE_UNSPECIFIED}, {@link #PASSWORD_MODE_NUMERIC},
+ * or {@link #PASSWORD_MODE_ALPHANUMERIC}.
+ */
+ public void setPasswordMode(ComponentName admin, int mode) {
+ if (mService != null) {
+ try {
+ mService.setPasswordMode(admin, mode);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current password mode that is in effect due to all
+ * device admins.
+ */
+ public int getPasswordMode() {
+ if (mService != null) {
+ try {
+ return mService.getPasswordMode();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return PASSWORD_MODE_UNSPECIFIED;
+ }
+
+ /**
+ * Retrieve the password mode associated with the last password the
+ * user selected.
+ */
+ public int getActivePasswordMode() {
+ if (mService != null) {
+ try {
+ return mService.getActivePasswordMode();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return PASSWORD_MODE_UNSPECIFIED;
+ }
+
+ /**
+ * Called by an application that is administering the device to set the
+ * minimum allowed password length. After setting this, the user
+ * will not be able to enter a new password that is not at least as
+ * restrictive as what has been set. Note that the current password
+ * will remain until the user has set a new one, so the change does not
+ * take place immediately. To prompt the user for a new password, use
+ * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+ * constraint is only imposed if the administrator has also requested either
+ * {@link #PASSWORD_MODE_NUMERIC} or {@link #PASSWORD_MODE_ALPHANUMERIC}
+ * with {@link #setPasswordMode}.
+ *
+ * @param admin Which {@link DeviceAdmin} this request is associated with.
+ * @param length The new desired minimum password length. A value of 0
+ * means there is no restriction.
+ */
+ public void setMinimumPasswordLength(ComponentName admin, int length) {
+ if (mService != null) {
+ try {
+ mService.setMinimumPasswordLength(admin, length);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current minimum password length that is in effect due to all
+ * device admins.
+ */
+ public int getMinimumPasswordLength() {
+ if (mService != null) {
+ try {
+ return mService.getMinimumPasswordLength();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the password length associated with the last password the
+ * user selected.
+ */
+ public int getActiveMinimumPasswordLength() {
+ if (mService != null) {
+ try {
+ return mService.getActiveMinimumPasswordLength();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the number of times the user has failed at entering a
+ * password since that last successful password entry.
+ */
+ public int getCurrentFailedPasswordAttempts() {
+ if (mService != null) {
+ try {
+ return mService.getCurrentFailedPasswordAttempts();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Called by an application that is administering the device to set the
+ * maximum time for user activity until the device will lock. This limits
+ * the length that the user can set. It takes effect immediately.
+ *
+ * @param admin Which {@link DeviceAdmin} this request is associated with.
+ * @param timeMs The new desired maximum time to lock in milliseconds.
+ * A value of 0 means there is no restriction.
+ */
+ public void setMaximumTimeToLock(ComponentName admin, long timeMs) {
+ if (mService != null) {
+ try {
+ mService.setMaximumTimeToLock(admin, timeMs);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current maximum time to lock that is in effect due to all
+ * device admins. Returns 0 if no maximum is set.
+ */
+ public long getMaximumTimeToLock() {
+ if (mService != null) {
+ try {
+ return mService.getMaximumTimeToLock();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Constant for {@link #wipeData}: perform a low-level format of data
+ * storage.
+ */
+ public static final int WIPE_LOW_LEVEL_FORMAT = 0x0001;
+
+ /**
+ * Constant for {@link #wipeData}: also wipe any external storage.
+ */
+ public static final int WIPE_EXTERNAL_STORAGE = 0x0002;
+
+ /**
+ * Ask the user date be wiped. This will cause the device to reboot,
+ * erasing all user data while next booting up.
+ *
+ * @param flags Bit mask of additional options: currently
+ * {@link #WIPE_LOW_LEVEL_FORMAT} and {@link #WIPE_EXTERNAL_STORAGE}.
+ */
+ public void wipeData(int flags) {
+ if (mService != null) {
+ try {
+ mService.wipeData(flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setActiveAdmin(ComponentName policyReceiver) {
+ if (mService != null) {
+ try {
+ mService.setActiveAdmin(policyReceiver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public ComponentName getActiveAdmin() {
+ if (mService != null) {
+ try {
+ return mService.getActiveAdmin();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public DeviceAdminInfo getActiveAdminInfo() {
+ ComponentName cn = getActiveAdmin();
+ if (cn == null) {
+ return null;
+ }
+
+ ActivityInfo ai;
+ try {
+ ai = mContext.getPackageManager().getReceiverInfo(cn,
+ PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to retrieve device policy " + cn, e);
+ return null;
+ }
+
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = ai;
+
+ try {
+ return new DeviceAdminInfo(mContext, ri);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Unable to parse device policy " + cn, e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to parse device policy " + cn, e);
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setActivePasswordState(int mode, int length) {
+ if (mService != null) {
+ try {
+ mService.setActivePasswordState(mode, length);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void reportFailedPasswordAttempt() {
+ if (mService != null) {
+ try {
+ mService.reportFailedPasswordAttempt();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void reportSuccessfulPasswordAttempt() {
+ if (mService != null) {
+ try {
+ mService.reportSuccessfulPasswordAttempt();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
new file mode 100644
index 0000000..f62647f
--- /dev/null
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -0,0 +1,49 @@
+/*
+**
+** 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.app;
+
+import android.content.ComponentName;
+
+/**
+ * Internal IPC interface to the device policy service.
+ * {@hide}
+ */
+interface IDevicePolicyManager {
+ void setPasswordMode(in ComponentName who, int mode);
+ int getPasswordMode();
+ int getActivePasswordMode();
+
+ void setMinimumPasswordLength(in ComponentName who, int length);
+ int getMinimumPasswordLength();
+ int getActiveMinimumPasswordLength();
+
+ int getCurrentFailedPasswordAttempts();
+
+ void setMaximumTimeToLock(in ComponentName who, long timeMs);
+ long getMaximumTimeToLock();
+
+ void wipeData(int flags);
+
+ void setActiveAdmin(in ComponentName policyReceiver);
+ ComponentName getActiveAdmin();
+ void removeActiveAdmin(in ComponentName policyReceiver);
+
+ void setActivePasswordState(int mode, int length);
+ void reportFailedPasswordAttempt();
+ void reportSuccessfulPasswordAttempt();
+}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 981145b..d25d670 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1838,8 +1838,10 @@
*
* TODO: Doing this every time we start global search is inefficient. Will fix that once
* we have settled on the right mechanism for finding the global search activity.
+ *
+ * @hide
*/
- private ComponentName getGlobalSearchActivity() {
+ public ComponentName getGlobalSearchActivity() {
Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> activities =
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 1034fab..1612ac9 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -1,3 +1,19 @@
+/*
+ * 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 android.app;
import org.xmlpull.v1.XmlPullParser;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0fafe5d..0b83f03 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1132,6 +1132,7 @@
* you're running long tasks.
*/
public static final String POWER_SERVICE = "power";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.WindowManager} for accessing the system's window
@@ -1141,6 +1142,7 @@
* @see android.view.WindowManager
*/
public static final String WINDOW_SERVICE = "window";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.LayoutInflater} for inflating layout resources in this
@@ -1150,6 +1152,7 @@
* @see android.view.LayoutInflater
*/
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.accounts.AccountManager} for receiving intents at a
@@ -1159,6 +1162,7 @@
* @see android.accounts.AccountManager
*/
public static final String ACCOUNT_SERVICE = "account";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.ActivityManager} for interacting with the global
@@ -1168,6 +1172,7 @@
* @see android.app.ActivityManager
*/
public static final String ACTIVITY_SERVICE = "activity";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.AlarmManager} for receiving intents at a
@@ -1177,6 +1182,7 @@
* @see android.app.AlarmManager
*/
public static final String ALARM_SERVICE = "alarm";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.NotificationManager} for informing the user of
@@ -1186,6 +1192,7 @@
* @see android.app.NotificationManager
*/
public static final String NOTIFICATION_SERVICE = "notification";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.accessibility.AccessibilityManager} for giving the user
@@ -1195,6 +1202,7 @@
* @see android.view.accessibility.AccessibilityManager
*/
public static final String ACCESSIBILITY_SERVICE = "accessibility";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.NotificationManager} for controlling keyguard.
@@ -1203,6 +1211,7 @@
* @see android.app.KeyguardManager
*/
public static final String KEYGUARD_SERVICE = "keyguard";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.location.LocationManager} for controlling location
@@ -1212,6 +1221,7 @@
* @see android.location.LocationManager
*/
public static final String LOCATION_SERVICE = "location";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.app.SearchManager} for handling searches.
@@ -1220,6 +1230,7 @@
* @see android.app.SearchManager
*/
public static final String SEARCH_SERVICE = "search";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.hardware.SensorManager} for accessing sensors.
@@ -1228,6 +1239,7 @@
* @see android.hardware.SensorManager
*/
public static final String SENSOR_SERVICE = "sensor";
+
/**
* Use with {@link #getSystemService} to retrieve a
* com.android.server.WallpaperService for accessing wallpapers.
@@ -1235,6 +1247,7 @@
* @see #getSystemService
*/
public static final String WALLPAPER_SERVICE = "wallpaper";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.os.Vibrator} for interacting with the vibration hardware.
@@ -1243,6 +1256,7 @@
* @see android.os.Vibrator
*/
public static final String VIBRATOR_SERVICE = "vibrator";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.app.StatusBarManager} for interacting with the status bar.
@@ -1340,6 +1354,15 @@
public static final String DROPBOX_SERVICE = "dropbox";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.app.DevicePolicyManager} for working with global
+ * device policy management.
+ *
+ * @see #getSystemService
+ */
+ public static final String DEVICE_POLICY_SERVICE = "device_policy";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index 62330e1..d71344c 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -34,7 +34,9 @@
import java.util.concurrent.atomic.AtomicInteger;
/**
- * A gesture can have a single or multiple strokes
+ * A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes.
+ * Each stroke is a sequence of timed points. A user-defined gesture can be recognized by
+ * a GestureLibrary and a built-in alphabet gesture can be recognized by a LetterRecognizer.
*/
public class Gesture implements Parcelable {
@@ -58,6 +60,19 @@
mGestureID = GESTURE_ID_BASE + sGestureCount.incrementAndGet();
}
+ @Override
+ public Object clone() {
+ Gesture gesture = new Gesture();
+ gesture.mBoundingBox.set(mBoundingBox.left, mBoundingBox.top,
+ mBoundingBox.right, mBoundingBox.bottom);
+ final int count = mStrokes.size();
+ for (int i = 0; i < count; i++) {
+ GestureStroke stroke = mStrokes.get(i);
+ gesture.mStrokes.add((GestureStroke)stroke.clone());
+ }
+ return gesture;
+ }
+
/**
* @return all the strokes of the gesture
*/
@@ -73,7 +88,7 @@
}
/**
- * Add a stroke to the gesture
+ * Adds a stroke to the gesture.
*
* @param stroke
*/
@@ -83,8 +98,8 @@
}
/**
- * Get the total length of the gesture. When there are multiple strokes in
- * the gesture, this returns the sum of the lengths of all the strokes
+ * Calculates the total length of the gesture. When there are multiple strokes in
+ * the gesture, this returns the sum of the lengths of all the strokes.
*
* @return the length of the gesture
*/
@@ -142,7 +157,7 @@
}
/**
- * Set the id of the gesture
+ * Sets the id of the gesture.
*
* @param id
*/
@@ -158,7 +173,7 @@
}
/**
- * Create a bitmap of the gesture with a transparent background
+ * Creates a bitmap of the gesture with a transparent background.
*
* @param width width of the target bitmap
* @param height height of the target bitmap
@@ -194,7 +209,7 @@
}
/**
- * Create a bitmap of the gesture with a transparent background
+ * Creates a bitmap of the gesture with a transparent background.
*
* @param width
* @param height
diff --git a/core/java/android/gesture/GesturePoint.java b/core/java/android/gesture/GesturePoint.java
index 3698011..4cb7707 100644
--- a/core/java/android/gesture/GesturePoint.java
+++ b/core/java/android/gesture/GesturePoint.java
@@ -20,7 +20,7 @@
import java.io.IOException;
/**
- * A timed point of a gesture stroke
+ * A timed point of a gesture stroke. Multiple points form a stroke.
*/
public class GesturePoint {
@@ -43,4 +43,9 @@
final long timeStamp = in.readLong();
return new GesturePoint(x, y, timeStamp);
}
+
+ @Override
+ public Object clone() {
+ return new GesturePoint(x, y, timestamp);
+ }
}
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 598eb85..68dc5a6 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -27,7 +27,8 @@
import java.util.ArrayList;
/**
- * A gesture stroke started on a touch down and ended on a touch up.
+ * A gesture stroke started on a touch down and ended on a touch up. A stroke
+ * consists of a sequence of timed points. One or multiple strokes form a gesture.
*/
public class GestureStroke {
static final float TOUCH_TOLERANCE = 8;
@@ -41,7 +42,7 @@
private Path mCachedPath;
/**
- * Construct a gesture stroke from a list of gesture points
+ * A constructor that constructs a gesture stroke from a list of gesture points.
*
* @param points
*/
@@ -82,7 +83,22 @@
}
/**
- * Draw the gesture with a given canvas and paint
+ * A faster constructor specially for cloning a stroke.
+ */
+ private GestureStroke(RectF bbx, float len, float[] pts, long[] times) {
+ boundingBox = new RectF(bbx.left, bbx.top, bbx.right, bbx.bottom);
+ length = len;
+ points = pts.clone();
+ timestamps = times.clone();
+ }
+
+ @Override
+ public Object clone() {
+ return new GestureStroke(boundingBox, length, points, timestamps);
+ }
+
+ /**
+ * Draws the stroke with a given canvas and paint.
*
* @param canvas
*/
@@ -134,7 +150,7 @@
}
/**
- * Convert the stroke to a Path based on the number of points
+ * Converts the stroke to a Path of a given number of points.
*
* @param width the width of the bounding box of the target path
* @param height the height of the bounding box of the target path
@@ -213,14 +229,15 @@
}
/**
- * Invalidate the cached path that is used to render the stroke
+ * Invalidates the cached path that is used to render the stroke.
*/
public void clearPath() {
if (mCachedPath != null) mCachedPath.rewind();
}
/**
- * Compute an oriented bounding box of the stroke
+ * Computes an oriented bounding box of the stroke.
+ *
* @return OrientedBoundingBox
*/
public OrientedBoundingBox computeOrientedBoundingBox() {
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index b532365..dfe1d00 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -48,6 +48,10 @@
}
}
}
+
+ static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension) {
+ return spatialSampling(gesture, sampleMatrixDimension, false);
+ }
static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension,
boolean uniformScaling) {
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index e73569a..c0c2d03 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -94,6 +94,11 @@
String mountSecureContainer(String id, String key, int ownerUid);
/*
+ * Unount a secure container.
+ */
+ void unmountSecureContainer(String id);
+
+ /*
* Returns the filesystem path of a mounted secure container.
*/
String getSecureContainerPath(String id);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
new file mode 100644
index 0000000..bd6cabb
--- /dev/null
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -0,0 +1,105 @@
+/* //device/java/android/android/os/INetworkManagementService.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * @hide
+ */
+interface INetworkManagementService
+{
+ /**
+ ** GENERAL
+ **/
+
+ /**
+ * Returns a list of currently known network interfaces
+ */
+ String[] listInterfaces();
+
+ /**
+ * Shuts down the service
+ */
+ void shutdown();
+
+ /**
+ ** TETHERING RELATED
+ **/
+
+
+ /**
+ * Returns true if IP forwarding is enabled
+ */
+ boolean getIpForwardingEnabled();
+
+ /**
+ * Enables/Disables IP Forwarding
+ */
+ void setIpForwardingEnabled(boolean enabled);
+
+ /**
+ * Start tethering services with the specified dhcp server range
+ */
+ void startTethering(String dhcpRangeStart, String dhcpRangeEnd);
+
+ /**
+ * Stop currently running tethering services
+ */
+ void stopTethering();
+
+ /**
+ * Returns true if tethering services are started
+ */
+ boolean isTetheringStarted();
+
+ /**
+ * Tethers the specified interface
+ */
+ void tetherInterface(String iface);
+
+ /**
+ * Untethers the specified interface
+ */
+ void untetherInterface(String iface);
+
+ /**
+ * Returns a list of currently tethered interfaces
+ */
+ String[] listTetheredInterfaces();
+
+ /**
+ * Sets the list of DNS forwarders (in order of priority)
+ */
+ void setDnsForwarders(in String[] dns);
+
+ /**
+ * Returns the list of DNS fowarders (in order of priority)
+ */
+ String[] getDnsForwarders();
+
+ /**
+ * Enables Network Address Translation between two interfaces.
+ * The address and netmask of the external interface is used for
+ * the NAT'ed network.
+ */
+ void enableNat(String internalInterface, String externalInterface);
+
+ /**
+ * Disables Network Address Translation between two interfaces.
+ */
+ void disableNat(String internalInterface, String externalInterface);
+}
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index c2928cb..fe8cfb0 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -522,7 +522,7 @@
protected void handleParams(String params) throws VCardException {
String[] strArray = params.split("=", 2);
if (strArray.length == 2) {
- String paramName = strArray[0].trim();
+ final String paramName = strArray[0].trim().toUpperCase();
String paramValue = strArray[1].trim();
if (paramName.equals("TYPE")) {
handleType(paramValue);
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 3774156..eb863ef 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -19,41 +19,45 @@
import android.net.Uri;
/**
- * Exposes constants used to interact with the download manager's
- * content provider.
- * The constants URI ... STATUS are the names of columns in the downloads table.
+ * The Download Manager
*
- * @hide
+ * @pending
*/
-// For 1.0 the download manager can't deal with abuse from untrusted apps, so
-// this API is hidden.
public final class Downloads {
+ /**
+ * @hide
+ */
private Downloads() {}
/**
* The permission to access the download manager
+ * @hide
*/
public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
/**
* The permission to access the download manager's advanced functions
+ * @hide
*/
public static final String PERMISSION_ACCESS_ADVANCED =
"android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
/**
* The permission to directly access the download manager's cache directory
+ * @hide
*/
public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
/**
* The permission to send broadcasts on download completion
+ * @hide
*/
public static final String PERMISSION_SEND_INTENTS =
"android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
/**
* The content:// URI for the data table in the provider
+ * @hide
*/
public static final Uri CONTENT_URI =
Uri.parse("content://downloads/download");
@@ -62,6 +66,7 @@
* Broadcast Action: this is sent by the download manager to the app
* that had initiated a download when that download completes. The
* download's content: uri is specified in the intent's data.
+ * @hide
*/
public static final String ACTION_DOWNLOAD_COMPLETED =
"android.intent.action.DOWNLOAD_COMPLETED";
@@ -75,6 +80,7 @@
* multiple downloads.
* Note: this is not currently sent for downloads that have completed
* successfully.
+ * @hide
*/
public static final String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
@@ -83,6 +89,7 @@
* The name of the column containing the URI of the data being downloaded.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_URI = "uri";
@@ -90,6 +97,7 @@
* The name of the column containing application-specific data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_APP_DATA = "entity";
@@ -103,6 +111,7 @@
* whether a download fully completed).
* <P>Type: BOOLEAN</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_NO_INTEGRITY = "no_integrity";
@@ -112,6 +121,7 @@
* to use this filename, or a variation, as the actual name for the file.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_FILE_NAME_HINT = "hint";
@@ -120,6 +130,7 @@
* was actually stored.
* <P>Type: TEXT</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String _DATA = "_data";
@@ -127,6 +138,7 @@
* The name of the column containing the MIME type of the downloaded data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_MIME_TYPE = "mimetype";
@@ -135,6 +147,7 @@
* of the download. See the DESTINATION_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_DESTINATION = "destination";
@@ -144,6 +157,7 @@
* a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_VISIBILITY = "visibility";
@@ -153,6 +167,7 @@
* the CONTROL_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_CONTROL = "control";
@@ -162,6 +177,7 @@
* the STATUS_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_STATUS = "status";
@@ -171,6 +187,7 @@
* value.
* <P>Type: BIGINT</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_LAST_MODIFICATION = "lastmod";
@@ -180,6 +197,7 @@
* notifications to a component in this package when the download completes.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
@@ -190,6 +208,7 @@
* Intent.setClassName(String,String).
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
@@ -198,6 +217,7 @@
* is sent to the specified class and package when a download has finished.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
@@ -207,6 +227,7 @@
* header that gets sent with the request.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_COOKIE_DATA = "cookiedata";
@@ -215,6 +236,7 @@
* application wants the download manager to use for this download.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_USER_AGENT = "useragent";
@@ -223,6 +245,7 @@
* application wants the download manager to use for this download.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_REFERER = "referer";
@@ -231,6 +254,7 @@
* downloaded.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_TOTAL_BYTES = "total_bytes";
@@ -239,6 +263,7 @@
* has been downloaded so far.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_CURRENT_BYTES = "current_bytes";
@@ -251,6 +276,7 @@
* android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_OTHER_UID = "otheruid";
@@ -260,6 +286,7 @@
* list of downloads.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_TITLE = "title";
@@ -269,6 +296,7 @@
* user in the list of downloads.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_DESCRIPTION = "description";
@@ -284,6 +312,7 @@
* Downloads to the external destination only write files for which
* there is a registered handler. The resulting files are accessible
* by filename to all applications.
+ * @hide
*/
public static final int DESTINATION_EXTERNAL = 0;
@@ -295,6 +324,7 @@
* application can access the file (indirectly through a content
* provider). This requires the
* android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
+ * @hide
*/
public static final int DESTINATION_CACHE_PARTITION = 1;
@@ -304,6 +334,7 @@
* for private files (similar to CACHE_PARTITION) that aren't deleted
* immediately after they are used, and are kept around by the download
* manager as long as space is available.
+ * @hide
*/
public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
@@ -311,16 +342,19 @@
* This download will be saved to the download manager's private
* partition, as with DESTINATION_CACHE_PARTITION, but the download
* will not proceed if the user is on a roaming data connection.
+ * @hide
*/
public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
/**
* This download is allowed to run.
+ * @hide
*/
public static final int CONTROL_RUN = 0;
/**
* This download must pause at the first opportunity.
+ * @hide
*/
public static final int CONTROL_PAUSED = 1;
@@ -337,6 +371,7 @@
/**
* Returns whether the status is informational (i.e. 1xx).
+ * @hide
*/
public static boolean isStatusInformational(int status) {
return (status >= 100 && status < 200);
@@ -346,6 +381,7 @@
* Returns whether the download is suspended. (i.e. whether the download
* won't complete without some action from outside the download
* manager).
+ * @hide
*/
public static boolean isStatusSuspended(int status) {
return (status == STATUS_PENDING_PAUSED || status == STATUS_RUNNING_PAUSED);
@@ -353,6 +389,7 @@
/**
* Returns whether the status is a success (i.e. 2xx).
+ * @hide
*/
public static boolean isStatusSuccess(int status) {
return (status >= 200 && status < 300);
@@ -360,6 +397,7 @@
/**
* Returns whether the status is an error (i.e. 4xx or 5xx).
+ * @hide
*/
public static boolean isStatusError(int status) {
return (status >= 400 && status < 600);
@@ -367,6 +405,7 @@
/**
* Returns whether the status is a client error (i.e. 4xx).
+ * @hide
*/
public static boolean isStatusClientError(int status) {
return (status >= 400 && status < 500);
@@ -374,6 +413,7 @@
/**
* Returns whether the status is a server error (i.e. 5xx).
+ * @hide
*/
public static boolean isStatusServerError(int status) {
return (status >= 500 && status < 600);
@@ -382,6 +422,7 @@
/**
* Returns whether the download has completed (either with success or
* error).
+ * @hide
*/
public static boolean isStatusCompleted(int status) {
return (status >= 200 && status < 300) || (status >= 400 && status < 600);
@@ -389,21 +430,25 @@
/**
* This download hasn't stated yet
+ * @hide
*/
public static final int STATUS_PENDING = 190;
/**
* This download hasn't stated yet and is paused
+ * @hide
*/
public static final int STATUS_PENDING_PAUSED = 191;
/**
* This download has started
+ * @hide
*/
public static final int STATUS_RUNNING = 192;
/**
* This download has started and is paused
+ * @hide
*/
public static final int STATUS_RUNNING_PAUSED = 193;
@@ -412,18 +457,21 @@
* Warning: there might be other status values that indicate success
* in the future.
* Use isSucccess() to capture the entire category.
+ * @hide
*/
public static final int STATUS_SUCCESS = 200;
/**
* This request couldn't be parsed. This is also used when processing
* requests with unknown/unsupported URI schemes.
+ * @hide
*/
public static final int STATUS_BAD_REQUEST = 400;
/**
* This download can't be performed because the content type cannot be
* handled.
+ * @hide
*/
public static final int STATUS_NOT_ACCEPTABLE = 406;
@@ -435,6 +483,7 @@
* client when a response is received whose length cannot be determined
* accurately (therefore making it impossible to know when a download
* completes).
+ * @hide
*/
public static final int STATUS_LENGTH_REQUIRED = 411;
@@ -442,11 +491,13 @@
* This download was interrupted and cannot be resumed.
* This is the code for the HTTP error "Precondition Failed", and it is
* also used in situations where the client doesn't have an ETag at all.
+ * @hide
*/
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
* This download was canceled
+ * @hide
*/
public static final int STATUS_CANCELED = 490;
@@ -454,12 +505,14 @@
* This download has completed with an error.
* Warning: there will be other status values that indicate errors in
* the future. Use isStatusError() to capture the entire category.
+ * @hide
*/
public static final int STATUS_UNKNOWN_ERROR = 491;
/**
* This download couldn't be completed because of a storage issue.
* Typically, that's because the filesystem is missing or full.
+ * @hide
*/
public static final int STATUS_FILE_ERROR = 492;
@@ -467,52 +520,66 @@
* This download couldn't be completed because of an HTTP
* redirect response that the download manager couldn't
* handle.
+ * @hide
*/
public static final int STATUS_UNHANDLED_REDIRECT = 493;
/**
* This download couldn't be completed because of an
* unspecified unhandled HTTP code.
+ * @hide
*/
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
/**
* This download couldn't be completed because of an
* error receiving or processing data at the HTTP level.
+ * @hide
*/
public static final int STATUS_HTTP_DATA_ERROR = 495;
/**
* This download couldn't be completed because of an
* HttpException while setting up the request.
+ * @hide
*/
public static final int STATUS_HTTP_EXCEPTION = 496;
/**
* This download couldn't be completed because there were
* too many redirects.
+ * @hide
*/
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
/**
* This download is visible but only shows in the notifications
* while it's in progress.
+ * @hide
*/
public static final int VISIBILITY_VISIBLE = 0;
/**
* This download is visible and shows in the notifications while
* in progress and after completion.
+ * @hide
*/
public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
/**
* This download doesn't show in the UI or in the notifications.
+ * @hide
*/
public static final int VISIBILITY_HIDDEN = 2;
/**
* Implementation details
+ *
+ * Exposes constants used to interact with the download manager's
+ * content provider.
+ * The constants URI ... STATUS are the names of columns in the downloads table.
+ *
+ * @hide
*/
public static final class Impl implements BaseColumns {
private Impl() {}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index fe3b149..eb48a0c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,6 +20,8 @@
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
@@ -58,9 +60,13 @@
public abstract class WallpaperService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
+ * To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
+ * that other applications can not abuse it.
*/
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
- "android.service.wallpaper.WallpaperService";
+ "android.service.wallpaper.WallpaperService";
/**
* Name under which a WallpaperService component publishes information
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
new file mode 100644
index 0000000..2da8f14
--- /dev/null
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -0,0 +1,360 @@
+/*
+ * 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.view;
+
+import android.content.Context;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
+ * callback will notify users when a particular gesture event has occurred.
+ * This class should only be used with {@link MotionEvent}s reported via touch.
+ *
+ * To use this class:
+ * <ul>
+ * <li>Create an instance of the {@code ScaleGestureDetector} for your
+ * {@link View}
+ * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ * callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class ScaleGestureDetector {
+ /**
+ * The listener for receiving notifications when gestures occur.
+ * If you want to listen for all the different gestures then implement
+ * this interface. If you only want to listen for a subset it might
+ * be easier to extend {@link SimpleOnScaleGestureListener}.
+ *
+ * An application will receive events in the following order:
+ * <ul>
+ * <li>One {@link OnScaleGestureListener#onScaleBegin()}
+ * <li>Zero or more {@link OnScaleGestureListener#onScale()}
+ * <li>One {@link OnScaleGestureListener#onTransformEnd()}
+ * </ul>
+ */
+ public interface OnScaleGestureListener {
+ /**
+ * Responds to scaling events for a gesture in progress.
+ * Reported by pointer motion.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ * @return Whether or not the detector should consider this event
+ * as handled. If an event was not handled, the detector
+ * will continue to accumulate movement until an event is
+ * handled. This can be useful if an application, for example,
+ * only wants to update scaling factors if the change is
+ * greater than 0.01.
+ */
+ public boolean onScale(ScaleGestureDetector detector);
+
+ /**
+ * Responds to the beginning of a scaling gesture. Reported by
+ * new pointers going down.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ * @return Whether or not the detector should continue recognizing
+ * this gesture. For example, if a gesture is beginning
+ * with a focal point outside of a region where it makes
+ * sense, onScaleBegin() may return false to ignore the
+ * rest of the gesture.
+ */
+ public boolean onScaleBegin(ScaleGestureDetector detector);
+
+ /**
+ * Responds to the end of a scale gesture. Reported by existing
+ * pointers going up. If the end of a gesture would result in a fling,
+ * {@link onTransformFling()} is called instead.
+ *
+ * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
+ * and {@link ScaleGestureDetector#getFocusY()} will return the location
+ * of the pointer remaining on the screen.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ */
+ public void onScaleEnd(ScaleGestureDetector detector);
+ }
+
+ /**
+ * A convenience class to extend when you only want to listen for a subset
+ * of scaling-related events. This implements all methods in
+ * {@link OnScaleGestureListener} but does nothing.
+ * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
+ * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
+ * {@code true}.
+ */
+ public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
+
+ public boolean onScale(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ // Intentionally empty
+ }
+ }
+
+ private static final float PRESSURE_THRESHOLD = 0.67f;
+
+ private Context mContext;
+ private OnScaleGestureListener mListener;
+ private boolean mGestureInProgress;
+
+ private MotionEvent mPrevEvent;
+ private MotionEvent mCurrEvent;
+
+ private float mFocusX;
+ private float mFocusY;
+ private float mPrevFingerDiffX;
+ private float mPrevFingerDiffY;
+ private float mCurrFingerDiffX;
+ private float mCurrFingerDiffY;
+ private float mCurrLen;
+ private float mPrevLen;
+ private float mScaleFactor;
+ private float mCurrPressure;
+ private float mPrevPressure;
+ private long mTimeDelta;
+
+ public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ mContext = context;
+ mListener = listener;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ final int action = event.getAction();
+ boolean handled = true;
+
+ if (!mGestureInProgress) {
+ if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+ action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+ event.getPointerCount() >= 2) {
+ // We have a new multi-finger gesture
+
+ // Be paranoid in case we missed an event
+ reset();
+
+ mPrevEvent = MotionEvent.obtain(event);
+ mTimeDelta = 0;
+
+ setContext(event);
+ mGestureInProgress = mListener.onScaleBegin(this);
+ }
+ } else {
+ // Transform gesture in progress - attempt to handle it
+ switch (action) {
+ case MotionEvent.ACTION_POINTER_1_UP:
+ case MotionEvent.ACTION_POINTER_2_UP:
+ // Gesture ended
+ setContext(event);
+
+ // Set focus point to the remaining finger
+ int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
+ >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+ mFocusX = event.getX(id);
+ mFocusY = event.getY(id);
+
+ mListener.onScaleEnd(this);
+ mGestureInProgress = false;
+
+ reset();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ mListener.onScaleEnd(this);
+ mGestureInProgress = false;
+
+ reset();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ setContext(event);
+
+ // Only accept the event if our relative pressure is within
+ // a certain limit - this can help filter shaky data as a
+ // finger is lifted.
+ if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
+ final boolean updatePrevious = mListener.onScale(this);
+
+ if (updatePrevious) {
+ mPrevEvent.recycle();
+ mPrevEvent = MotionEvent.obtain(event);
+ }
+ }
+ break;
+ }
+ }
+ return handled;
+ }
+
+ private void setContext(MotionEvent curr) {
+ if (mCurrEvent != null) {
+ mCurrEvent.recycle();
+ }
+ mCurrEvent = MotionEvent.obtain(curr);
+
+ mCurrLen = -1;
+ mPrevLen = -1;
+ mScaleFactor = -1;
+
+ final MotionEvent prev = mPrevEvent;
+
+ final float px0 = prev.getX(0);
+ final float py0 = prev.getY(0);
+ final float px1 = prev.getX(1);
+ final float py1 = prev.getY(1);
+ final float cx0 = curr.getX(0);
+ final float cy0 = curr.getY(0);
+ final float cx1 = curr.getX(1);
+ final float cy1 = curr.getY(1);
+
+ final float pvx = px1 - px0;
+ final float pvy = py1 - py0;
+ final float cvx = cx1 - cx0;
+ final float cvy = cy1 - cy0;
+ mPrevFingerDiffX = pvx;
+ mPrevFingerDiffY = pvy;
+ mCurrFingerDiffX = cvx;
+ mCurrFingerDiffY = cvy;
+
+ mFocusX = cx0 + cvx * 0.5f;
+ mFocusY = cy0 + cvy * 0.5f;
+ mTimeDelta = curr.getEventTime() - prev.getEventTime();
+ mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
+ mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
+ }
+
+ private void reset() {
+ if (mPrevEvent != null) {
+ mPrevEvent.recycle();
+ mPrevEvent = null;
+ }
+ if (mCurrEvent != null) {
+ mCurrEvent.recycle();
+ mCurrEvent = null;
+ }
+ }
+
+ /**
+ * Returns {@code true} if a two-finger scale gesture is in progress.
+ * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+ */
+ public boolean isInProgress() {
+ return mGestureInProgress;
+ }
+
+ /**
+ * Get the X coordinate of the current gesture's focal point.
+ * If a gesture is in progress, the focal point is directly between
+ * the two pointers forming the gesture.
+ * If a gesture is ending, the focal point is the location of the
+ * remaining pointer on the screen.
+ * If {@link isInProgress()} would return false, the result of this
+ * function is undefined.
+ *
+ * @return X coordinate of the focal point in pixels.
+ */
+ public float getFocusX() {
+ return mFocusX;
+ }
+
+ /**
+ * Get the Y coordinate of the current gesture's focal point.
+ * If a gesture is in progress, the focal point is directly between
+ * the two pointers forming the gesture.
+ * If a gesture is ending, the focal point is the location of the
+ * remaining pointer on the screen.
+ * If {@link isInProgress()} would return false, the result of this
+ * function is undefined.
+ *
+ * @return Y coordinate of the focal point in pixels.
+ */
+ public float getFocusY() {
+ return mFocusY;
+ }
+
+ /**
+ * Return the current distance between the two pointers forming the
+ * gesture in progress.
+ *
+ * @return Distance between pointers in pixels.
+ */
+ public float getCurrentSpan() {
+ if (mCurrLen == -1) {
+ final float cvx = mCurrFingerDiffX;
+ final float cvy = mCurrFingerDiffY;
+ mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+ }
+ return mCurrLen;
+ }
+
+ /**
+ * Return the previous distance between the two pointers forming the
+ * gesture in progress.
+ *
+ * @return Previous distance between pointers in pixels.
+ */
+ public float getPreviousSpan() {
+ if (mPrevLen == -1) {
+ final float pvx = mPrevFingerDiffX;
+ final float pvy = mPrevFingerDiffY;
+ mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+ }
+ return mPrevLen;
+ }
+
+ /**
+ * Return the scaling factor from the previous scale event to the current
+ * event. This value is defined as
+ * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
+ *
+ * @return The current scaling factor.
+ */
+ public float getScaleFactor() {
+ if (mScaleFactor == -1) {
+ mScaleFactor = getCurrentSpan() / getPreviousSpan();
+ }
+ return mScaleFactor;
+ }
+
+ /**
+ * Return the time difference in milliseconds between the previous
+ * accepted scaling event and the current scaling event.
+ *
+ * @return Time difference since the last scaling event in milliseconds.
+ */
+ public long getTimeDelta() {
+ return mTimeDelta;
+ }
+
+ /**
+ * Return the event time of the current event being processed.
+ *
+ * @return Current event time in milliseconds.
+ */
+ public long getEventTime() {
+ return mCurrEvent.getEventTime();
+ }
+}
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
deleted file mode 100644
index 196716a..0000000
--- a/core/java/android/view/TransformGestureDetector.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * 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.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.GestureDetector.SimpleOnGestureListener;
-
-/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
- * will notify users when a particular gesture event has occurred. This class
- * should only be used with {@link MotionEvent}s reported via touch.
- *
- * To use this class:
- * <ul>
- * <li>Create an instance of the {@code TransformGestureDetector} for your
- * {@link View}
- * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
- * callback will be executed when the events occur.
- * </ul>
- * @hide Pending API approval
- */
-public class TransformGestureDetector {
- /**
- * The listener for receiving notifications when gestures occur.
- * If you want to listen for all the different gestures then implement
- * this interface. If you only want to listen for a subset it might
- * be easier to extend {@link SimpleOnGestureListener}.
- *
- * An application will receive events in the following order:
- * One onTransformBegin()
- * Zero or more onTransform()
- * One onTransformEnd() or onTransformFling()
- */
- public interface OnTransformGestureListener {
- /**
- * Responds to transformation events for a gesture in progress.
- * Reported by pointer motion.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransform(TransformGestureDetector detector);
-
- /**
- * Responds to the beginning of a transformation gesture. Reported by
- * new pointers going down.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformBegin(TransformGestureDetector detector);
-
- /**
- * Responds to the end of a transformation gesture. Reported by existing
- * pointers going up. If the end of a gesture would result in a fling,
- * onTransformFling is called instead.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformEnd(TransformGestureDetector detector);
-
- /**
- * Responds to the end of a transformation gesture that begins a fling.
- * Reported by existing pointers going up. If the end of a gesture
- * would not result in a fling, onTransformEnd is called instead.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformFling(TransformGestureDetector detector);
- }
-
- private static final boolean DEBUG = false;
-
- private static final int INITIAL_EVENT_IGNORES = 2;
-
- private Context mContext;
- private float mTouchSizeScale;
- private OnTransformGestureListener mListener;
- private int mVelocityTimeUnits;
- private MotionEvent mInitialEvent;
-
- private MotionEvent mPrevEvent;
- private MotionEvent mCurrEvent;
- private VelocityTracker mVelocityTracker;
-
- private float mCenterX;
- private float mCenterY;
- private float mTransX;
- private float mTransY;
- private float mPrevFingerDiffX;
- private float mPrevFingerDiffY;
- private float mCurrFingerDiffX;
- private float mCurrFingerDiffY;
- private float mRotateDegrees;
- private float mCurrLen;
- private float mPrevLen;
- private float mScaleFactor;
-
- // Units in pixels. Current value is pulled out of thin air for debugging only.
- private float mPointerJumpLimit = 30;
-
- private int mEventIgnoreCount;
-
- public TransformGestureDetector(Context context, OnTransformGestureListener listener,
- int velocityTimeUnits) {
- mContext = context;
- mListener = listener;
- mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
- mVelocityTimeUnits = velocityTimeUnits;
- mEventIgnoreCount = INITIAL_EVENT_IGNORES;
- }
-
- public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
- this(context, listener, 1000);
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- boolean handled = true;
-
- if (mInitialEvent == null) {
- // No transform gesture in progress
- if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
- action == MotionEvent.ACTION_POINTER_2_DOWN) &&
- event.getPointerCount() >= 2) {
- // We have a new multi-finger gesture
- mInitialEvent = MotionEvent.obtain(event);
- mPrevEvent = MotionEvent.obtain(event);
- mVelocityTracker = VelocityTracker.obtain();
- handled = mListener.onTransformBegin(this);
- }
- } else {
- // Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_1_UP:
- case MotionEvent.ACTION_POINTER_2_UP:
- // Gesture ended
- handled = mListener.onTransformEnd(this);
-
- reset();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- handled = mListener.onTransformEnd(this);
-
- reset();
- break;
-
- case MotionEvent.ACTION_MOVE:
- setContext(event);
-
- // Our first few events can be crazy from some touchscreens - drop them.
- if (mEventIgnoreCount == 0) {
- mVelocityTracker.addMovement(event);
- handled = mListener.onTransform(this);
- } else {
- mEventIgnoreCount--;
- }
-
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- break;
- }
- }
- return handled;
- }
-
- private void setContext(MotionEvent curr) {
- mCurrEvent = MotionEvent.obtain(curr);
-
- mRotateDegrees = -1;
- mCurrLen = -1;
- mPrevLen = -1;
- mScaleFactor = -1;
-
- final MotionEvent prev = mPrevEvent;
-
- float px0 = prev.getX(0);
- float py0 = prev.getY(0);
- float px1 = prev.getX(1);
- float py1 = prev.getY(1);
- float cx0 = curr.getX(0);
- float cy0 = curr.getY(0);
- float cx1 = curr.getX(1);
- float cy1 = curr.getY(1);
-
- // Some touchscreens do weird things with pointer values where points are
- // too close along one axis. Try to detect this here and smooth things out.
- // The main indicator is that we get the X or Y value from the other pointer.
- final float dx0 = cx0 - px0;
- final float dy0 = cy0 - py0;
- final float dx1 = cx1 - px1;
- final float dy1 = cy1 - py1;
-
- if (cx0 == cx1) {
- if (Math.abs(dx0) > mPointerJumpLimit) {
- cx0 = px0;
- } else if (Math.abs(dx1) > mPointerJumpLimit) {
- cx1 = px1;
- }
- } else if (cy0 == cy1) {
- if (Math.abs(dy0) > mPointerJumpLimit) {
- cy0 = py0;
- } else if (Math.abs(dy1) > mPointerJumpLimit) {
- cy1 = py1;
- }
- }
-
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- mPrevFingerDiffX = pvx;
- mPrevFingerDiffY = pvy;
- mCurrFingerDiffX = cvx;
- mCurrFingerDiffY = cvy;
-
- final float pmidx = px0 + pvx * 0.5f;
- final float pmidy = py0 + pvy * 0.5f;
- final float cmidx = cx0 + cvx * 0.5f;
- final float cmidy = cy0 + cvy * 0.5f;
-
- mCenterX = cmidx;
- mCenterY = cmidy;
- mTransX = cmidx - pmidx;
- mTransY = cmidy - pmidy;
- }
-
- private void reset() {
- if (mInitialEvent != null) {
- mInitialEvent.recycle();
- mInitialEvent = null;
- }
- if (mPrevEvent != null) {
- mPrevEvent.recycle();
- mPrevEvent = null;
- }
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- mCurrEvent = null;
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mEventIgnoreCount = INITIAL_EVENT_IGNORES;
- }
-
- public float getCenterX() {
- return mCenterX;
- }
-
- public float getCenterY() {
- return mCenterY;
- }
-
- public float getTranslateX() {
- return mTransX;
- }
-
- public float getTranslateY() {
- return mTransY;
- }
-
- public float getCurrentSpan() {
- if (mCurrLen == -1) {
- final float cvx = mCurrFingerDiffX;
- final float cvy = mCurrFingerDiffY;
- mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
- }
- return mCurrLen;
- }
-
- public float getPreviousSpan() {
- if (mPrevLen == -1) {
- final float pvx = mPrevFingerDiffX;
- final float pvy = mPrevFingerDiffY;
- mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
- }
- return mPrevLen;
- }
-
- public float getScaleFactor() {
- if (mScaleFactor == -1) {
- mScaleFactor = getCurrentSpan() / getPreviousSpan();
- }
- return mScaleFactor;
- }
-
- public float getRotation() {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index a5e0e94..2ddf5f8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -16,6 +16,8 @@
package android.view.inputmethod;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ResultReceiver;
@@ -54,9 +56,12 @@
/**
* This is the interface name that a service implementing an input
* method should say that it supports -- that is, this is the action it
- * uses for its intent filter. (Note: this name is used because this
- * interface should be moved to the view package.)
+ * uses for its intent filter.
+ * To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission so
+ * that other applications can not abuse it.
*/
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.view.InputMethod";
/**
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 37372c5..0ce70fa 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -50,6 +50,7 @@
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
+ @ViewDebug.ExportedProperty
private boolean mBaselineAligned = true;
/**
@@ -59,6 +60,7 @@
* Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
* with whether the children of this layout are baseline aligned.
*/
+ @ViewDebug.ExportedProperty
private int mBaselineAlignedChildIndex = -1;
/**
@@ -66,12 +68,30 @@
* We'll calculate the baseline of this layout as we measure vertically; for
* horizontal linear layouts, the offset of 0 is appropriate.
*/
+ @ViewDebug.ExportedProperty
private int mBaselineChildTop = 0;
+ @ViewDebug.ExportedProperty
private int mOrientation;
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = -1, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
+ @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
+ @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
+ @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
+ })
private int mGravity = Gravity.LEFT | Gravity.TOP;
+ @ViewDebug.ExportedProperty
private int mTotalLength;
+ @ViewDebug.ExportedProperty
private float mWeightSum;
private int[] mMaxAscent;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c49a86a..d81476a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -927,21 +927,27 @@
android:description="@string/permdesc_readInputState"
android:protectionLevel="signature" />
- <!-- Must be required by input method services, to ensure that only the
- system can bind to them. -->
+ <!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
+ to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_INPUT_METHOD"
android:label="@string/permlab_bindInputMethod"
android:description="@string/permdesc_bindInputMethod"
android:protectionLevel="signature" />
- <!-- Must be required by wallpaper services, to ensure that only the
- system can bind to them.
- @hide Live Wallpaper -->
+ <!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
+ to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_WALLPAPER"
android:label="@string/permlab_bindWallpaper"
android:description="@string/permdesc_bindWallpaper"
android:protectionLevel="signatureOrSystem" />
+ <!-- Must be required by device administration receiver, to ensure that only the
+ system can interact with it. -->
+ <permission android:name="android.permission.BIND_DEVICE_ADMIN"
+ android:label="@string/permlab_bindDeviceAdmin"
+ android:description="@string/permdesc_bindDeviceAdmin"
+ android:protectionLevel="signature" />
+
<!-- Allows low-level access to setting the orientation (actually
rotation) of the screen. Not for use by normal applications. -->
<permission android:name="android.permission.SET_ORIENTATION"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 53bb586..3dbfa25 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -131,7 +131,8 @@
the first component from this list which is found to be installed is set as the
preferred activity. -->
<string-array name="default_web_search_providers">
- <item>com.android.quicksearchbox/com.android.googlesearch.GoogleSearch</item>
+ <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item>
+ <item>com.android.quicksearchbox/.google.GoogleSearch</item>
<item>com.google.android.providers.enhancedgooglesearch/.Launcher</item>
<item>com.android.googlesearch/.GoogleSearch</item>
<item>com.android.websearch/.Search.1</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 539db83..265dacd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -617,6 +617,12 @@
interface of a wallpaper. Should never be needed for normal applications.</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_bindDeviceAdmin">interact with a device admin</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_bindDeviceAdmin">Allows the holder to send intents to
+ a device administrator. Should never be needed for normal applications.</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_setOrientation">change screen orientation</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_setOrientation">Allows an application to change
diff --git a/keystore/tests/src/android/security/SystemKeyStoreTest.java b/keystore/tests/src/android/security/SystemKeyStoreTest.java
index bbeceeb7..a9e2687 100644
--- a/keystore/tests/src/android/security/SystemKeyStoreTest.java
+++ b/keystore/tests/src/android/security/SystemKeyStoreTest.java
@@ -60,7 +60,7 @@
public void testBasicAccess() throws Exception {
try {
- byte[] newKey = mSysKeyStore.generateNewKey(128, "AES", keyName);
+ byte[] newKey = mSysKeyStore.generateNewKey(128, "Blowfish", keyName);
assertNotNull(newKey);
byte[] recKey = mSysKeyStore.retrieveKey(keyName);
assertEquals(newKey.length, recKey.length);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index e36e78c..26b9357 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -29,6 +29,7 @@
MPEG4Extractor.cpp \
MPEG4Writer.cpp \
MediaExtractor.cpp \
+ SampleIterator.cpp \
SampleTable.cpp \
ShoutcastSource.cpp \
StagefrightMediaScanner.cpp \
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 07a5a82..0e9900b 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -232,8 +232,9 @@
uint32_t sampleIndex;
uint32_t sampleTime;
if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
- && track->sampleTable->getDecodingTime(
- sampleIndex, &sampleTime) == OK) {
+ && track->sampleTable->getMetaDataForSample(
+ sampleIndex, NULL /* offset */, NULL /* size */,
+ &sampleTime) == OK) {
track->meta->setInt64(
kKeyThumbnailTime,
((int64_t)sampleTime * 1000000) / track->timescale);
@@ -929,20 +930,16 @@
if (mBuffer == NULL) {
newBuffer = true;
- status_t err = mSampleTable->getSampleOffsetAndSize(
- mCurrentSampleIndex, &offset, &size);
-
- if (err != OK) {
- return err;
- }
-
- err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+ status_t err =
+ mSampleTable->getMetaDataForSample(
+ mCurrentSampleIndex, &offset, &size, &dts);
if (err != OK) {
return err;
}
err = mGroup->acquire_buffer(&mBuffer);
+
if (err != OK) {
CHECK_EQ(mBuffer, NULL);
return err;
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
new file mode 100644
index 0000000..faad42ba
--- /dev/null
+++ b/media/libstagefright/SampleIterator.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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 "SampleIterator"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/SampleIterator.h"
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/SampleTable.h"
+
+namespace android {
+
+SampleIterator::SampleIterator(SampleTable *table)
+ : mTable(table),
+ mInitialized(false),
+ mTimeToSampleIndex(0),
+ mTTSSampleIndex(0),
+ mTTSSampleTime(0),
+ mTTSCount(0),
+ mTTSDuration(0) {
+ reset();
+}
+
+void SampleIterator::reset() {
+ mSampleToChunkIndex = 0;
+ mFirstChunk = 0;
+ mFirstChunkSampleIndex = 0;
+ mStopChunk = 0;
+ mStopChunkSampleIndex = 0;
+ mSamplesPerChunk = 0;
+ mChunkDesc = 0;
+}
+
+status_t SampleIterator::seekTo(uint32_t sampleIndex) {
+ LOGV("seekTo(%d)", sampleIndex);
+
+ if (mTable->mSampleToChunkOffset < 0
+ || mTable->mChunkOffsetOffset < 0
+ || mTable->mSampleSizeOffset < 0
+ || mTable->mTimeToSampleCount == 0) {
+
+ return ERROR_MALFORMED;
+ }
+
+ if (mInitialized && mCurrentSampleIndex == sampleIndex) {
+ return OK;
+ }
+
+ if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) {
+ reset();
+ }
+
+ if (sampleIndex >= mStopChunkSampleIndex) {
+ status_t err;
+ if ((err = findChunkRange(sampleIndex)) != OK) {
+ LOGE("findChunkRange failed");
+ return err;
+ }
+ }
+
+ CHECK(sampleIndex < mStopChunkSampleIndex);
+
+ uint32_t chunk =
+ (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk
+ + mFirstChunk;
+
+ if (!mInitialized || chunk != mCurrentChunkIndex) {
+ mCurrentChunkIndex = chunk;
+
+ status_t err;
+ if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {
+ LOGE("getChunkOffset return error");
+ return err;
+ }
+
+ mCurrentChunkSampleSizes.clear();
+
+ uint32_t firstChunkSampleIndex =
+ mFirstChunkSampleIndex
+ + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);
+
+ for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {
+ size_t sampleSize;
+ if ((err = getSampleSizeDirect(
+ firstChunkSampleIndex + i, &sampleSize)) != OK) {
+ LOGE("getSampleSizeDirect return error");
+ return err;
+ }
+
+ mCurrentChunkSampleSizes.push(sampleSize);
+ }
+ }
+
+ uint32_t chunkRelativeSampleIndex =
+ (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk;
+
+ mCurrentSampleOffset = mCurrentChunkOffset;
+ for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) {
+ mCurrentSampleOffset += mCurrentChunkSampleSizes[i];
+ }
+
+ mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex];
+ if (sampleIndex < mTTSSampleIndex) {
+ mTimeToSampleIndex = 0;
+ mTTSSampleIndex = 0;
+ mTTSSampleTime = 0;
+ mTTSCount = 0;
+ mTTSDuration = 0;
+ }
+
+ status_t err;
+ if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
+ LOGE("findSampleTime return error");
+ return err;
+ }
+
+ mCurrentSampleIndex = sampleIndex;
+
+ mInitialized = true;
+
+ return OK;
+}
+
+status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
+ CHECK(sampleIndex >= mFirstChunkSampleIndex);
+
+ while (sampleIndex >= mStopChunkSampleIndex) {
+ if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mFirstChunkSampleIndex = mStopChunkSampleIndex;
+
+ const SampleTable::SampleToChunkEntry *entry =
+ &mTable->mSampleToChunkEntries[mSampleToChunkIndex];
+
+ mFirstChunk = entry->startChunk;
+ mSamplesPerChunk = entry->samplesPerChunk;
+ mChunkDesc = entry->chunkDesc;
+
+ if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
+ mStopChunk = entry[1].startChunk;
+
+ mStopChunkSampleIndex =
+ mFirstChunkSampleIndex
+ + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
+ } else {
+ mStopChunk = 0xffffffff;
+ mStopChunkSampleIndex = 0xffffffff;
+ }
+
+ ++mSampleToChunkIndex;
+ }
+
+ return OK;
+}
+
+status_t SampleIterator::getChunkOffset(uint32_t chunk, off_t *offset) {
+ *offset = 0;
+
+ if (chunk >= mTable->mNumChunkOffsets) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) {
+ uint32_t offset32;
+
+ if (mTable->mDataSource->readAt(
+ mTable->mChunkOffsetOffset + 8 + 4 * chunk,
+ &offset32,
+ sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntohl(offset32);
+ } else {
+ CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64);
+
+ uint64_t offset64;
+ if (mTable->mDataSource->readAt(
+ mTable->mChunkOffsetOffset + 8 + 8 * chunk,
+ &offset64,
+ sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntoh64(offset64);
+ }
+
+ return OK;
+}
+
+status_t SampleIterator::getSampleSizeDirect(
+ uint32_t sampleIndex, size_t *size) {
+ *size = 0;
+
+ if (sampleIndex >= mTable->mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mTable->mDefaultSampleSize > 0) {
+ *size = mTable->mDefaultSampleSize;
+ return OK;
+ }
+
+ switch (mTable->mSampleSizeFieldSize) {
+ case 32:
+ {
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
+ size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
+ return ERROR_IO;
+ }
+
+ *size = ntohl(*size);
+ break;
+ }
+
+ case 16:
+ {
+ uint16_t x;
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + 2 * sampleIndex,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *size = ntohs(x);
+ break;
+ }
+
+ case 8:
+ {
+ uint8_t x;
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + sampleIndex,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *size = x;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
+
+ uint8_t x;
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + sampleIndex / 2,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *size = (sampleIndex & 1) ? x & 0x0f : x >> 4;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleIterator::findSampleTime(
+ uint32_t sampleIndex, uint32_t *time) {
+ if (sampleIndex >= mTable->mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ while (sampleIndex >= mTTSSampleIndex + mTTSCount) {
+ if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mTTSSampleIndex += mTTSCount;
+ mTTSSampleTime += mTTSCount * mTTSDuration;
+
+ mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];
+ mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];
+
+ ++mTimeToSampleIndex;
+ }
+
+ *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 2de96d4..89a522e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -15,9 +15,11 @@
*/
#define LOG_TAG "SampleTable"
+//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include "include/SampleTable.h"
+#include "include/SampleIterator.h"
#include <arpa/inet.h>
@@ -27,10 +29,16 @@
namespace android {
-static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
-static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
-static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
-static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+// static
+const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+// static
+const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+// static
+const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+// static
+const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+////////////////////////////////////////////////////////////////////////////////
SampleTable::SampleTable(const sp<DataSource> &source)
: mDataSource(source),
@@ -46,12 +54,20 @@
mTimeToSampleCount(0),
mTimeToSample(NULL),
mSyncSampleOffset(-1),
- mNumSyncSamples(0) {
+ mNumSyncSamples(0),
+ mSampleToChunkEntries(NULL) {
+ mSampleIterator = new SampleIterator(this);
}
SampleTable::~SampleTable() {
+ delete[] mSampleToChunkEntries;
+ mSampleToChunkEntries = NULL;
+
delete[] mTimeToSample;
mTimeToSample = NULL;
+
+ delete mSampleIterator;
+ mSampleIterator = NULL;
}
status_t SampleTable::setChunkOffsetParams(
@@ -124,6 +140,25 @@
return ERROR_MALFORMED;
}
+ mSampleToChunkEntries =
+ new SampleToChunkEntry[mNumSampleToChunkOffsets];
+
+ for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
+ uint8_t buffer[12];
+ if (mDataSource->readAt(
+ mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
+ != (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ CHECK(U32_AT(buffer) >= 1); // chunk index is 1 based in the spec.
+
+ // We want the chunk index to be 0-based.
+ mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
+ mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
+ mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
+ }
+
return OK;
}
@@ -250,217 +285,10 @@
return mNumChunkOffsets;
}
-status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
- *offset = 0;
-
- if (mChunkOffsetOffset < 0) {
- return ERROR_MALFORMED;
- }
-
- if (chunk_index >= mNumChunkOffsets) {
- return ERROR_OUT_OF_RANGE;
- }
-
- if (mChunkOffsetType == kChunkOffsetType32) {
- uint32_t offset32;
-
- if (mDataSource->readAt(
- mChunkOffsetOffset + 8 + 4 * chunk_index,
- &offset32,
- sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
- return ERROR_IO;
- }
-
- *offset = ntohl(offset32);
- } else {
- CHECK_EQ(mChunkOffsetType, kChunkOffsetType64);
-
- uint64_t offset64;
- if (mDataSource->readAt(
- mChunkOffsetOffset + 8 + 8 * chunk_index,
- &offset64,
- sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
- return ERROR_IO;
- }
-
- *offset = ntoh64(offset64);
- }
-
- return OK;
-}
-
-status_t SampleTable::getChunkForSample(
- uint32_t sample_index,
- uint32_t *chunk_index,
- uint32_t *chunk_relative_sample_index,
- uint32_t *desc_index) {
- *chunk_index = 0;
- *chunk_relative_sample_index = 0;
- *desc_index = 0;
-
- if (mSampleToChunkOffset < 0) {
- return ERROR_MALFORMED;
- }
-
- if (sample_index >= countSamples()) {
- return ERROR_END_OF_STREAM;
- }
-
- uint32_t first_chunk = 0;
- uint32_t samples_per_chunk = 0;
- uint32_t chunk_desc_index = 0;
-
- uint32_t index = 0;
- while (index < mNumSampleToChunkOffsets) {
- uint8_t buffer[12];
- if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12,
- buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
- return ERROR_IO;
- }
-
- uint32_t stop_chunk = U32_AT(buffer);
- if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) {
- break;
- }
-
- sample_index -= (stop_chunk - first_chunk) * samples_per_chunk;
- first_chunk = stop_chunk;
- samples_per_chunk = U32_AT(&buffer[4]);
- chunk_desc_index = U32_AT(&buffer[8]);
-
- ++index;
- }
-
- *chunk_index = sample_index / samples_per_chunk + first_chunk - 1;
- *chunk_relative_sample_index = sample_index % samples_per_chunk;
- *desc_index = chunk_desc_index;
-
- return OK;
-}
-
uint32_t SampleTable::countSamples() const {
return mNumSampleSizes;
}
-status_t SampleTable::getSampleSize(
- uint32_t sample_index, size_t *sample_size) {
- *sample_size = 0;
-
- if (mSampleSizeOffset < 0) {
- return ERROR_MALFORMED;
- }
-
- if (sample_index >= mNumSampleSizes) {
- return ERROR_OUT_OF_RANGE;
- }
-
- if (mDefaultSampleSize > 0) {
- *sample_size = mDefaultSampleSize;
- return OK;
- }
-
- switch (mSampleSizeFieldSize) {
- case 32:
- {
- if (mDataSource->readAt(
- mSampleSizeOffset + 12 + 4 * sample_index,
- sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
- return ERROR_IO;
- }
-
- *sample_size = ntohl(*sample_size);
- break;
- }
-
- case 16:
- {
- uint16_t x;
- if (mDataSource->readAt(
- mSampleSizeOffset + 12 + 2 * sample_index,
- &x, sizeof(x)) < (ssize_t)sizeof(x)) {
- return ERROR_IO;
- }
-
- *sample_size = ntohs(x);
- break;
- }
-
- case 8:
- {
- uint8_t x;
- if (mDataSource->readAt(
- mSampleSizeOffset + 12 + sample_index,
- &x, sizeof(x)) < (ssize_t)sizeof(x)) {
- return ERROR_IO;
- }
-
- *sample_size = x;
- break;
- }
-
- default:
- {
- CHECK_EQ(mSampleSizeFieldSize, 4);
-
- uint8_t x;
- if (mDataSource->readAt(
- mSampleSizeOffset + 12 + sample_index / 2,
- &x, sizeof(x)) < (ssize_t)sizeof(x)) {
- return ERROR_IO;
- }
-
- *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4;
- break;
- }
- }
-
- return OK;
-}
-
-status_t SampleTable::getSampleOffsetAndSize(
- uint32_t sample_index, off_t *offset, size_t *size) {
- Mutex::Autolock autoLock(mLock);
-
- *offset = 0;
- *size = 0;
-
- uint32_t chunk_index;
- uint32_t chunk_relative_sample_index;
- uint32_t desc_index;
- status_t err = getChunkForSample(
- sample_index, &chunk_index, &chunk_relative_sample_index,
- &desc_index);
-
- if (err != OK) {
- return err;
- }
-
- err = getChunkOffset(chunk_index, offset);
-
- if (err != OK) {
- return err;
- }
-
- for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) {
- size_t sample_size;
- err = getSampleSize(sample_index - j - 1, &sample_size);
-
- if (err != OK) {
- return err;
- }
-
- *offset += sample_size;
- }
-
- err = getSampleSize(sample_index, size);
-
- if (err != OK) {
- return err;
- }
-
- return OK;
-}
-
status_t SampleTable::getMaxSampleSize(size_t *max_size) {
Mutex::Autolock autoLock(mLock);
@@ -468,7 +296,7 @@
for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
size_t sample_size;
- status_t err = getSampleSize(i, &sample_size);
+ status_t err = getSampleSize_l(i, &sample_size);
if (err != OK) {
return err;
@@ -482,34 +310,6 @@
return OK;
}
-status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) {
- // XXX FIXME idiotic (for the common use-case) O(n) algorithm below...
-
- Mutex::Autolock autoLock(mLock);
-
- if (sample_index >= mNumSampleSizes) {
- return ERROR_OUT_OF_RANGE;
- }
-
- uint32_t cur_sample = 0;
- *time = 0;
- for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
- uint32_t n = mTimeToSample[2 * i];
- uint32_t delta = mTimeToSample[2 * i + 1];
-
- if (sample_index < cur_sample + n) {
- *time += delta * (sample_index - cur_sample);
-
- return OK;
- }
-
- *time += delta * n;
- cur_sample += n;
- }
-
- return ERROR_OUT_OF_RANGE;
-}
-
uint32_t abs_difference(uint32_t time1, uint32_t time2) {
return time1 > time2 ? time1 - time2 : time2 - time1;
}
@@ -539,7 +339,7 @@
}
if (flags & kSyncSample_Flag) {
- return findClosestSyncSample(*sample_index, sample_index);
+ return findClosestSyncSample_l(*sample_index, sample_index);
}
return OK;
@@ -552,7 +352,7 @@
return ERROR_OUT_OF_RANGE;
}
-status_t SampleTable::findClosestSyncSample(
+status_t SampleTable::findClosestSyncSample_l(
uint32_t start_sample_index, uint32_t *sample_index) {
*sample_index = 0;
@@ -590,6 +390,8 @@
}
status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
+ Mutex::Autolock autoLock(mLock);
+
if (mSyncSampleOffset < 0) {
// All samples are sync-samples.
*sample_index = 0;
@@ -620,7 +422,7 @@
// Now x is a sample index.
size_t sampleSize;
- status_t err = getSampleSize(x, &sampleSize);
+ status_t err = getSampleSize_l(x, &sampleSize);
if (err != OK) {
return err;
}
@@ -636,5 +438,38 @@
return OK;
}
+status_t SampleTable::getSampleSize_l(
+ uint32_t sampleIndex, size_t *sampleSize) {
+ return mSampleIterator->getSampleSizeDirect(
+ sampleIndex, sampleSize);
+}
+
+status_t SampleTable::getMetaDataForSample(
+ uint32_t sampleIndex,
+ off_t *offset,
+ size_t *size,
+ uint32_t *decodingTime) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t err;
+ if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
+ return err;
+ }
+
+ if (offset) {
+ *offset = mSampleIterator->getSampleOffset();
+ }
+
+ if (size) {
+ *size = mSampleIterator->getSampleSize();
+ }
+
+ if (decodingTime) {
+ *decodingTime = mSampleIterator->getSampleTime();
+ }
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
new file mode 100644
index 0000000..a5eaed9
--- /dev/null
+++ b/media/libstagefright/include/SampleIterator.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include <utils/Vector.h>
+
+namespace android {
+
+struct SampleTable;
+
+struct SampleIterator {
+ SampleIterator(SampleTable *table);
+
+ status_t seekTo(uint32_t sampleIndex);
+
+ uint32_t getChunkIndex() const { return mCurrentChunkIndex; }
+ uint32_t getDescIndex() const { return mChunkDesc; }
+ off_t getSampleOffset() const { return mCurrentSampleOffset; }
+ size_t getSampleSize() const { return mCurrentSampleSize; }
+ uint32_t getSampleTime() const { return mCurrentSampleTime; }
+
+ status_t getSampleSizeDirect(
+ uint32_t sampleIndex, size_t *size);
+
+private:
+ SampleTable *mTable;
+
+ bool mInitialized;
+
+ uint32_t mSampleToChunkIndex;
+ uint32_t mFirstChunk;
+ uint32_t mFirstChunkSampleIndex;
+ uint32_t mStopChunk;
+ uint32_t mStopChunkSampleIndex;
+ uint32_t mSamplesPerChunk;
+ uint32_t mChunkDesc;
+
+ uint32_t mCurrentChunkIndex;
+ off_t mCurrentChunkOffset;
+ Vector<size_t> mCurrentChunkSampleSizes;
+
+ uint32_t mTimeToSampleIndex;
+ uint32_t mTTSSampleIndex;
+ uint32_t mTTSSampleTime;
+ uint32_t mTTSCount;
+ uint32_t mTTSDuration;
+
+ uint32_t mCurrentSampleIndex;
+ off_t mCurrentSampleOffset;
+ size_t mCurrentSampleSize;
+ uint32_t mCurrentSampleTime;
+
+ void reset();
+ status_t findChunkRange(uint32_t sampleIndex);
+ status_t getChunkOffset(uint32_t chunk, off_t *offset);
+ status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
+
+ SampleIterator(const SampleIterator &);
+ SampleIterator &operator=(const SampleIterator &);
+};
+
+} // namespace android
+
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index ead3431..533ce84 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -28,6 +28,7 @@
namespace android {
class DataSource;
+struct SampleIterator;
class SampleTable : public RefBase {
public:
@@ -50,21 +51,16 @@
////////////////////////////////////////////////////////////////////////////
uint32_t countChunkOffsets() const;
- status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
-
- status_t getChunkForSample(
- uint32_t sample_index, uint32_t *chunk_index,
- uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
uint32_t countSamples() const;
- status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
-
- status_t getSampleOffsetAndSize(
- uint32_t sample_index, off_t *offset, size_t *size);
status_t getMaxSampleSize(size_t *size);
- status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
+ status_t getMetaDataForSample(
+ uint32_t sampleIndex,
+ off_t *offset,
+ size_t *size,
+ uint32_t *decodingTime);
enum {
kSyncSample_Flag = 1
@@ -72,15 +68,17 @@
status_t findClosestSample(
uint32_t req_time, uint32_t *sample_index, uint32_t flags);
- status_t findClosestSyncSample(
- uint32_t start_sample_index, uint32_t *sample_index);
-
status_t findThumbnailSample(uint32_t *sample_index);
protected:
~SampleTable();
private:
+ static const uint32_t kChunkOffsetType32;
+ static const uint32_t kChunkOffsetType64;
+ static const uint32_t kSampleSizeType32;
+ static const uint32_t kSampleSizeTypeCompact;
+
sp<DataSource> mDataSource;
Mutex mLock;
@@ -102,6 +100,22 @@
off_t mSyncSampleOffset;
uint32_t mNumSyncSamples;
+ SampleIterator *mSampleIterator;
+
+ struct SampleToChunkEntry {
+ uint32_t startChunk;
+ uint32_t samplesPerChunk;
+ uint32_t chunkDesc;
+ };
+ SampleToChunkEntry *mSampleToChunkEntries;
+
+ friend struct SampleIterator;
+
+ status_t findClosestSyncSample_l(
+ uint32_t start_sample_index, uint32_t *sample_index);
+
+ status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
+
SampleTable(const SampleTable &);
SampleTable &operator=(const SampleTable &);
};
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 8c3f252..0d617a5 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -135,50 +135,6 @@
////////////////////////////////////////////////////////////////////////////////
-class BufferMeta {
-public:
- BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
- : mOwner(owner),
- mMem(mem),
- mIsBackup(is_backup) {
- }
-
- BufferMeta(OMX *owner, size_t size)
- : mOwner(owner),
- mSize(size),
- mIsBackup(false) {
- }
-
- void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
- if (!mIsBackup) {
- return;
- }
-
- memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
- header->pBuffer + header->nOffset,
- header->nFilledLen);
- }
-
- void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
- if (!mIsBackup) {
- return;
- }
-
- memcpy(header->pBuffer + header->nOffset,
- (const OMX_U8 *)mMem->pointer() + header->nOffset,
- header->nFilledLen);
- }
-
-private:
- OMX *mOwner;
- sp<IMemory> mMem;
- size_t mSize;
- bool mIsBackup;
-
- BufferMeta(const BufferMeta &);
- BufferMeta &operator=(const BufferMeta &);
-};
-
OMX::OMX()
: mMaster(new OMXMaster),
mDispatcher(new CallbackDispatcher(this)),
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 2e23899..5b45c1c 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -586,14 +586,14 @@
double r = uniform_rand();
- if (r < 0.5) {
+ if (i > 0 && r < 0.5) {
// 50% chance of just continuing to decode from last position.
requestedSeekTimeUs = -1;
LOGI("requesting linear read");
} else {
- if (r < 0.55) {
+ if (i > 0 && r < 0.55) {
// 5% chance of seeking beyond end of stream.
requestedSeekTimeUs = durationUs;
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index c77424f..322f743 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -86,7 +86,7 @@
static int mount(const char* path) {
String16 string(path);
- gMountService->mountMedia(string);
+ gMountService->mountVolume(string);
for (int i = 0; i < 60; i++) {
if (isMounted(path)) {
@@ -129,6 +129,11 @@
return 0;
}
+static void asec_unmount(const char *id) {
+ String16 sId(id);
+ gMountService->unmountSecureContainer(sId);
+}
+
static int asec_path(const char *id) {
String16 sId(id);
gMountService->getSecureContainerPath(sId);
@@ -137,7 +142,7 @@
static int unmount(const char* path) {
String16 string(path);
- gMountService->unmountMedia(string);
+ gMountService->unmountVolume(string);
for (int i = 0; i < 20; i++) {
if (!isMounted(path)) {
@@ -155,7 +160,7 @@
if (isMounted(path))
return -EBUSY;
- gMountService->formatMedia(string);
+ gMountService->formatVolume(string);
return 0;
}
@@ -208,6 +213,9 @@
return android::asec_destroy(id);
} else if (!strcmp(argument, "mount")) {
return android::asec_mount(id, argv[4], atoi(argv[5]));
+ } else if (!strcmp(argument, "unmount")) {
+ android::asec_unmount(id);
+ return 0;
} else if (!strcmp(argument, "path")) {
return android::asec_path(id);
}
@@ -224,6 +232,7 @@
" sdutil asec finalize <id>\n"
" sdutil asec destroy <id>\n"
" sdutil asec mount <id> <key> <ownerUid>\n"
+ " sdutil asec unmount <id>\n"
" sdutil asec path <id>\n"
);
return -1;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 471625e..a186e58 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -157,6 +157,7 @@
*
* @see #getRenderMode()
* @see #setRenderMode(int)
+ * @see #requestRender()
*/
public final static int RENDERMODE_WHEN_DIRTY = 0;
/**
@@ -165,7 +166,6 @@
*
* @see #getRenderMode()
* @see #setRenderMode(int)
- * @see #requestRender()
*/
public final static int RENDERMODE_CONTINUOUSLY = 1;
@@ -210,6 +210,9 @@
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
+ // setType is not needed for SDK 2.0 or newer. Uncomment this
+ // statement if back-porting this code to older SDKs.
+ // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
}
/**
@@ -579,7 +582,7 @@
* Called when the surface is created or recreated.
* <p>
* Called when the rendering thread
- * starts and whenever the EGL context is lost. The context will typically
+ * starts and whenever the EGL context is lost. The EGL context will typically
* be lost when the Android device awakes after going to sleep.
* <p>
* Since this method is called at the beginning of rendering, as well as
@@ -871,7 +874,7 @@
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
- public void start(){
+ public void start() {
/*
* Get an EGL instance
*/
@@ -896,8 +899,8 @@
mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
/*
- * Create an OpenGL ES context. This must be done only once, an
- * OpenGL context is a somewhat heavy object.
+ * Create an EGL context. We want to do this as rarely as we can, because an
+ * EGL context is a somewhat heavy object.
*/
mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
@@ -1010,6 +1013,7 @@
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
+
}
/**
@@ -1031,6 +1035,7 @@
mRenderer = renderer;
}
+
@Override
public void run() {
setName("GLThread " + getId());
@@ -1051,20 +1056,33 @@
* This private method should only be called inside a
* synchronized(sGLThreadManager) block.
*/
- private void stopEglLocked() {
- if (mHaveEgl) {
- mHaveEgl = false;
+ private void stopEglSurfaceLocked() {
+ if (mHaveEglSurface) {
+ mHaveEglSurface = false;
mEglHelper.destroySurface();
- mEglHelper.finish();
- sGLThreadManager.releaseEglSurfaceLocked(this);
}
}
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglContextLocked() {
+ if (mHaveEglContext) {
+ mEglHelper.finish();
+ mHaveEglContext = false;
+ sGLThreadManager.releaseEglContextLocked(this);
+ }
+ }
private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper();
+ mHaveEglContext = false;
+ mHaveEglSurface = false;
try {
GL10 gl = null;
+ boolean createEglContext = false;
boolean createEglSurface = false;
+ boolean lostEglContext = false;
boolean sizeChanged = false;
boolean wantRenderNotification = false;
boolean doRenderNotification = false;
@@ -1084,12 +1102,25 @@
break;
}
+ // Have we lost the EGL context?
+ if (lostEglContext) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ lostEglContext = false;
+ }
+
// Do we need to release the EGL surface?
- if (mHaveEgl && mPaused) {
+ if (mHaveEglSurface && mPaused) {
if (LOG_SURFACE) {
Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
}
- stopEglLocked();
+ stopEglSurfaceLocked();
+ if (sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
+ stopEglContextLocked();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+ }
+ }
}
// Have we lost the surface view surface?
@@ -1097,8 +1128,8 @@
if (LOG_SURFACE) {
Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
}
- if (mHaveEgl) {
- stopEglLocked();
+ if (mHaveEglSurface) {
+ stopEglSurfaceLocked();
}
mWaitingForSurface = true;
sGLThreadManager.notifyAll();
@@ -1125,16 +1156,22 @@
&& (mWidth > 0) && (mHeight > 0)
&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
- // If we don't have an egl surface, try to acquire one.
- if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
- mHaveEgl = true;
+ // If we don't have an EGL context, try to acquire one.
+ if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglContextLocked(this)) {
+ mHaveEglContext = true;
+ createEglContext = true;
mEglHelper.start();
- createEglSurface = true;
- sizeChanged = true;
+
sGLThreadManager.notifyAll();
}
- if (mHaveEgl) {
+ if (mHaveEglContext && !mHaveEglSurface) {
+ mHaveEglSurface = true;
+ createEglSurface = true;
+ sizeChanged = true;
+ }
+
+ if (mHaveEglSurface) {
if (mSizeChanged) {
sizeChanged = true;
w = mWidth;
@@ -1177,10 +1214,14 @@
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceCreated");
}
- mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
createEglSurface = false;
}
+ if (createEglContext) {
+ mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ createEglContext = false;
+ }
+
if (sizeChanged) {
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
@@ -1193,22 +1234,25 @@
Log.w("GLThread", "onDrawFrame");
}
mRenderer.onDrawFrame(gl);
- if(!mEglHelper.swap()) {
+ if (!mEglHelper.swap()) {
if (LOG_SURFACE) {
- Log.i("GLThread", "egl surface lost tid=" + getId());
+ Log.i("GLThread", "egl context lost tid=" + getId());
}
+ lostEglContext = true;
}
if (wantRenderNotification) {
doRenderNotification = true;
}
}
+
} finally {
/*
* clean-up everything...
*/
synchronized (sGLThreadManager) {
- stopEglLocked();
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
}
}
}
@@ -1338,13 +1382,15 @@
private boolean mPaused;
private boolean mHasSurface;
private boolean mWaitingForSurface;
- private boolean mHaveEgl;
+ private boolean mHaveEglContext;
+ private boolean mHaveEglSurface;
private int mWidth;
private int mHeight;
private int mRenderMode;
private boolean mRequestRender;
private boolean mRenderComplete;
private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+
// End of member variables protected by the sGLThreadManager monitor.
private Renderer mRenderer;
@@ -1406,12 +1452,12 @@
/*
* Tries once to acquire the right to use an EGL
- * surface. Does not block. Requires that we are already
+ * context. Does not block. Requires that we are already
* in the sGLThreadManager monitor when this is called.
*
- * @return true if the right to use an EGL surface was acquired.
+ * @return true if the right to use an EGL context was acquired.
*/
- public boolean tryAcquireEglSurfaceLocked(GLThread thread) {
+ public boolean tryAcquireEglContextLocked(GLThread thread) {
if (mEglOwner == thread || mEglOwner == null) {
mEglOwner = thread;
notifyAll();
@@ -1423,17 +1469,23 @@
}
return false;
}
+
/*
- * Releases the EGL surface. Requires that we are already in the
+ * Releases the EGL context. Requires that we are already in the
* sGLThreadManager monitor when this is called.
*/
- public void releaseEglSurfaceLocked(GLThread thread) {
+ public void releaseEglContextLocked(GLThread thread) {
if (mEglOwner == thread) {
mEglOwner = null;
}
notifyAll();
}
+ public synchronized boolean shouldReleaseEGLContextWhenPausing() {
+ checkGLESVersion();
+ return mMultipleGLESContextsAllowed;
+ }
+
public synchronized void checkGLDriver(GL10 gl) {
if (! mGLESDriverCheckComplete) {
checkGLESVersion();
@@ -1457,14 +1509,12 @@
}
mGLESVersionCheckComplete = true;
}
-
}
private boolean mGLESVersionCheckComplete;
private int mGLESVersion;
private boolean mGLESDriverCheckComplete;
private boolean mMultipleGLESContextsAllowed;
- private int mGLContextCount;
private static final int kGLES_20 = 0x20000;
private static final String kMSM7K_RENDERER_PREFIX =
"Q3Dimension MSM7500 ";
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
new file mode 100644
index 0000000..e13ddc8
--- /dev/null
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -0,0 +1,471 @@
+/*
+ * 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.server;
+
+import com.android.common.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.app.DeviceAdmin;
+import android.app.DeviceAdminInfo;
+import android.app.DevicePolicyManager;
+import android.app.IDevicePolicyManager;
+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.util.Log;
+import android.util.Xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Implementation of the device policy APIs.
+ */
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ private static final String TAG = "DevicePolicyManagerService";
+
+ private final Context mContext;
+
+ int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ int mActivePasswordLength = 0;
+ int mFailedPasswordAttempts = 0;
+
+ ActiveAdmin mActiveAdmin;
+
+ static class ActiveAdmin {
+ 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;
+ }
+
+ /**
+ * Instantiates the service.
+ */
+ public DevicePolicyManagerService(Context context) {
+ mContext = context;
+ }
+
+ ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
+ if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingPid()) {
+ if (who != null) {
+ if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
+ || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
+ throw new SecurityException("Current admin is not " + who);
+ }
+ }
+ return mActiveAdmin;
+ }
+ throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
+ }
+
+
+ void sendAdminCommandLocked(ActiveAdmin policy, String action) {
+ Intent intent = new Intent(action);
+ intent.setComponent(policy.info.getComponent());
+ mContext.sendBroadcast(intent);
+ }
+
+ ComponentName getActiveAdminLocked() {
+ if (mActiveAdmin != null) {
+ return mActiveAdmin.info.getComponent();
+ }
+ return null;
+ }
+
+ void removeActiveAdminLocked(ComponentName adminReceiver) {
+ ComponentName cur = getActiveAdminLocked();
+ if (cur != null && cur.equals(adminReceiver)) {
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
+ // XXX need to wait for it to complete.
+ mActiveAdmin = null;
+ }
+ }
+
+ public DeviceAdminInfo findAdmin(ComponentName adminName) {
+ Intent resolveIntent = new Intent();
+ resolveIntent.setComponent(adminName);
+ List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
+ resolveIntent, PackageManager.GET_META_DATA);
+ if (infos == null || infos.size() <= 0) {
+ throw new IllegalArgumentException("Unknown admin: " + adminName);
+ }
+
+ try {
+ return new DeviceAdminInfo(mContext, infos.get(0));
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Bad device admin requested: " + adminName, e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Bad device admin requested: " + adminName, e);
+ return null;
+ }
+ }
+
+ private static JournaledFile makeJournaledFile() {
+ final String base = "/data/system/device_policies.xml";
+ return new JournaledFile(new File(base), new File(base + ".tmp"));
+ }
+
+ private void saveSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(journal.chooseForWrite(), false);
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "policies");
+
+ ActiveAdmin ap = mActiveAdmin;
+ 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");
+ }
+ out.endTag(null, "admin");
+ }
+ out.endTag(null, "policies");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
+ private void loadSettingsLocked() {
+ 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();
+ }
+ 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;
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ }
+ } catch (NullPointerException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IOException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (!success) {
+ Log.w(TAG, "No valid start tag found in policies file");
+ }
+ }
+
+ public void systemReady() {
+ synchronized (this) {
+ loadSettingsLocked();
+ }
+ }
+
+ public void setActiveAdmin(ComponentName adminReceiver) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ DeviceAdminInfo info = findAdmin(adminReceiver);
+ if (info == null) {
+ throw new IllegalArgumentException("Bad admin: " + adminReceiver);
+ }
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName cur = getActiveAdminLocked();
+ if (cur != null && cur.equals(adminReceiver)) {
+ throw new IllegalStateException("An admin is already set");
+ }
+ if (cur != null) {
+ removeActiveAdminLocked(adminReceiver);
+ }
+ mActiveAdmin = new ActiveAdmin(info);
+ saveSettingsLocked();
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public ComponentName getActiveAdmin() {
+ synchronized (this) {
+ return getActiveAdminLocked();
+ }
+ }
+
+ public void removeActiveAdmin(ComponentName adminReceiver) {
+ synchronized (this) {
+ if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeActiveAdminLocked(adminReceiver);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setPasswordMode(ComponentName who, int mode) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+ if (ap.passwordMode != mode) {
+ ap.passwordMode = mode;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMode() {
+ synchronized (this) {
+ return mActiveAdmin != null ? mActiveAdmin.passwordMode
+ : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ }
+ }
+
+ public int getActivePasswordMode() {
+ 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);
+ return mActivePasswordMode;
+ }
+ }
+
+ public void setMinimumPasswordLength(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+ if (ap.minimumPasswordLength != length) {
+ ap.minimumPasswordLength = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getMinimumPasswordLength() {
+ synchronized (this) {
+ return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
+ }
+ }
+
+ public int getActiveMinimumPasswordLength() {
+ 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);
+ return mActivePasswordLength;
+ }
+ }
+
+ public int getCurrentFailedPasswordAttempts() {
+ 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);
+ return mFailedPasswordAttempts;
+ }
+ }
+
+ public void setMaximumTimeToLock(ComponentName who, long timeMs) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+ if (ap.maximumTimeToUnlock != timeMs) {
+ ap.maximumTimeToUnlock = timeMs;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public long getMaximumTimeToLock() {
+ synchronized (this) {
+ return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
+ }
+ }
+
+ 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);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Log.w(TAG, "*************** WIPE DATA HERE");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setActivePasswordState(int mode, int length) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ if (mActivePasswordMode != mode || mActivePasswordLength != length
+ || mFailedPasswordAttempts != 0) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mActivePasswordMode = mode;
+ mActivePasswordLength = length;
+ mFailedPasswordAttempts = 0;
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_PASSWORD_CHANGED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+
+ public void reportFailedPasswordAttempt() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mFailedPasswordAttempts++;
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_PASSWORD_FAILED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void reportSuccessfulPasswordAttempt() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ if (mFailedPasswordAttempts != 0) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mFailedPasswordAttempts = 0;
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 406897d..1c82c94 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -1203,12 +1203,10 @@
// Remove expired alerts
if (intentsToRemove != null) {
for (PendingIntent i : intentsToRemove) {
- mProximityAlerts.remove(i);
- ProximityAlert alert = mProximityAlerts.get(i);
+ ProximityAlert alert = mProximityAlerts.remove(i);
mProximitiesEntered.remove(alert);
}
}
-
}
// Note: this is called with the lock held.
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c8a6915..f8f8742 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -127,6 +127,7 @@
private boolean mUmsConnected = false;
private boolean mUmsEnabled = false;
+ private boolean mUmsEnabling = false;
private String mLegacyState = Environment.MEDIA_REMOVED;
@@ -332,13 +333,16 @@
String vp = Environment.getExternalStorageDirectory().getPath();
String vs = getVolumeState(vp);
+ mUmsEnabling = enable;
if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
unmountVolume(vp);
+ mUmsEnabling = false;
updateUsbMassStorageNotification(true, false);
}
setShareMethodEnabled(vp, "ums", enable);
mUmsEnabled = enable;
+ mUmsEnabling = false;
if (!enable) {
mountVolume(vp);
if (mPromptUms) {
@@ -594,10 +598,14 @@
} else if (newState == VolumeState.NoMedia) {
// NoMedia is handled via Disk Remove events
} else if (newState == VolumeState.Idle) {
- // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
+ /*
+ * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
+ * if we're in the process of enabling UMS
+ */
if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
!vs.equals(Environment.MEDIA_NOFS) &&
- !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
+ !vs.equals(Environment.MEDIA_UNMOUNTABLE) &&
+ !mUmsEnabling) {
notifyMediaUnmounted(mountPoint);
}
} else if (newState == VolumeState.Pending) {
@@ -1049,6 +1057,11 @@
return getSecureContainerPath(id);
}
+ public void unmountSecureContainer(String id) throws IllegalStateException {
+ String cmd = String.format("unmount_asec %s ", id);
+ mConnector.doCommand(cmd);
+ }
+
public String getSecureContainerPath(String id) throws IllegalStateException {
ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
new file mode 100644
index 0000000..97fa0cc
--- /dev/null
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.INetworkManagementService;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import java.util.ArrayList;
+
+import android.provider.Settings;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+
+import java.io.File;
+import java.io.FileReader;
+import java.lang.IllegalStateException;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * @hide
+ */
+class NetworkManagementService extends INetworkManagementService.Stub {
+
+ private static final String TAG = "NetworkManagmentService";
+
+ class NetdResponseCode {
+ public static final int InterfaceListResult = 110;
+ public static final int TetherInterfaceListResult = 111;
+ public static final int TetherDnsFwdTgtListResult = 112;
+
+ public static final int TetherStatusResult = 210;
+ public static final int IpFwdStatusResult = 211;
+ }
+
+ /**
+ * Binder context for this service
+ */
+ private Context mContext;
+
+ /**
+ * connector object for communicating with netd
+ */
+ private NativeDaemonConnector mConnector;
+
+ /**
+ * Constructs a new NetworkManagementService instance
+ *
+ * @param context Binder context for this service
+ */
+ private NetworkManagementService(Context context) {
+ mContext = context;
+
+ mConnector = new NativeDaemonConnector(
+ new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
+ Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+ thread.start();
+ }
+
+ //
+ // Netd Callback handling
+ //
+
+ class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
+ public void onDaemonConnected() {
+ new Thread() {
+ public void run() {
+ // XXX: Run some tests
+ }
+ }.start();
+ }
+ public boolean onEvent(int code, String raw, String[] cooked) {
+ return false;
+ }
+ }
+
+ //
+ // INetworkManagementService members
+ //
+
+ public String[] listInterfaces() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ ArrayList<String> rsp = mConnector.doCommand("list_interfaces");
+
+ String[] rdata = new String[rsp.size()];
+ int idx = 0;
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code == NetdResponseCode.InterfaceListResult) {
+ if (tok.length !=2) {
+ throw new IllegalStateException(
+ String.format("Malformatted list entry '%s'", line));
+ }
+ rdata[idx++] = tok[1];
+ } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+ return rdata;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ throw new IllegalStateException("Got an empty response");
+ }
+
+ public void shutdown() {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.SHUTDOWN)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires SHUTDOWN permission");
+ }
+
+ Log.d(TAG, "Shutting down");
+ }
+
+ public boolean getIpForwardingEnabled() throws IllegalStateException{
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code == NetdResponseCode.IpFwdStatusResult) {
+ // 211 Forwarding <enabled/disabled>
+ if (tok.length !=2) {
+ throw new IllegalStateException(
+ String.format("Malformatted list entry '%s'", line));
+ }
+ if (tok[2].equals("enabled"))
+ return true;
+ return false;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ throw new IllegalStateException("Got an empty response");
+ }
+
+ public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
+ }
+
+ public void startTethering(String dhcpRangeStart, String dhcpRangeEnd)
+ throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd));
+ }
+
+ public void stopTethering() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand("tether stop");
+ }
+
+ public boolean isTetheringStarted() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ ArrayList<String> rsp = mConnector.doCommand("tether status");
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code == NetdResponseCode.TetherStatusResult) {
+ // XXX: Tethering services <started/stopped> <TBD>...
+ if (tok[2].equals("started"))
+ return true;
+ return false;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ throw new IllegalStateException("Got an empty response");
+ }
+
+ public void tetherInterface(String iface) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand("tether interface add " + iface);
+ }
+
+ public void untetherInterface(String iface) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand("tether interface remove " + iface);
+ }
+
+ public String[] listTetheredInterfaces() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ ArrayList<String> rsp = mConnector.doCommand("tether interface list");
+
+ String[] rdata = new String[rsp.size()];
+ int idx = 0;
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code == NetdResponseCode.TetherInterfaceListResult) {
+ if (tok.length !=2) {
+ throw new IllegalStateException(
+ String.format("Malformatted list entry '%s'", line));
+ }
+ rdata[idx++] = tok[1];
+ } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+ return rdata;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ throw new IllegalStateException("Got an empty response");
+ }
+
+ public void setDnsForwarders(String[] dns) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ try {
+ String cmd = "tether dns set ";
+ for (String s : dns) {
+ cmd += InetAddress.getByName(s).toString() + " ";
+ }
+ mConnector.doCommand(cmd);
+ } catch (UnknownHostException e) {
+ throw new IllegalStateException("Error resolving dns name", e);
+ }
+ }
+
+ public String[] getDnsForwarders() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ ArrayList<String> rsp = mConnector.doCommand("tether dns list");
+
+ String[] rdata = new String[rsp.size()];
+ int idx = 0;
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code == NetdResponseCode.TetherDnsFwdTgtListResult) {
+ if (tok.length !=2) {
+ throw new IllegalStateException(
+ String.format("Malformatted list entry '%s'", line));
+ }
+ rdata[idx++] = tok[1];
+ } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+ return rdata;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ throw new IllegalStateException("Got an empty response");
+ }
+
+ public void enableNat(String internalInterface, String externalInterface)
+ throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand(
+ String.format("nat enable %s %s", internalInterface, externalInterface));
+ }
+
+ public void disableNat(String internalInterface, String externalInterface)
+ throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mConnector.doCommand(
+ String.format("nat disable %s %s", internalInterface, externalInterface));
+ }
+}
+
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 170477f..1b6a56a 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -75,6 +75,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.security.SystemKeyStore;
import android.util.*;
import android.view.Display;
import android.view.WindowManager;
@@ -89,6 +90,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -7439,7 +7441,8 @@
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
- final private String mSdEncryptKey = "none";
+ final private String mSdEncryptKey = "AppsOnSD";
+ final private String mSdEncryptAlg = "AES";
private MountService getMountService() {
return (MountService) ServiceManager.getService("mount");
@@ -7457,10 +7460,25 @@
String cachePath = null;
// Remove any pending destroy messages
mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+ String sdEncKey;
+ try {
+ 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 for package: " + pkgName + ".");
+ return null;
+ }
+ }
+ } catch (NoSuchAlgorithmException nsae) {
+ Log.e(TAG, "Failed to create encryption keys with exception: " + nsae);
+ return null;
+ }
try {
cachePath = mountService.createSecureContainer(pkgName,
mbLen,
- "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+ "vfat", sdEncKey, Process.SYSTEM_UID);
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
return cachePath;
} catch(IllegalStateException e) {
@@ -7477,7 +7495,7 @@
try {
cachePath = mountService.createSecureContainer(pkgName,
mbLen,
- "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+ "vfat", sdEncKey, Process.SYSTEM_UID);
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath);
return cachePath;
} catch(IllegalStateException e) {
@@ -7487,8 +7505,13 @@
}
private String mountSdDir(String pkgName, int ownerUid) {
+ String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+ if (sdEncKey == null) {
+ Log.e(TAG, "Failed to retrieve encryption keys to mount package code: " + pkgName + ".");
+ return null;
+ }
try {
- return getMountService().mountSecureContainer(pkgName, mSdEncryptKey, ownerUid);
+ return getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
} catch (IllegalStateException e) {
Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 674ade9..6b3f433 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -201,6 +201,7 @@
Log.e("System", "Failure starting core service", e);
}
+ DevicePolicyManagerService devicePolicy = null;
StatusBarService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
@@ -209,16 +210,25 @@
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
+ Log.i(TAG, "Device Policy");
+ devicePolicy = new DevicePolicyManagerService(context);
+ ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting DevicePolicyService", e);
+ }
+
+ try {
Log.i(TAG, "Status Bar");
statusBar = new StatusBarService(context);
- ServiceManager.addService("statusbar", statusBar);
+ ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
Log.e(TAG, "Failure starting StatusBarService", e);
}
try {
Log.i(TAG, "Clipboard Service");
- ServiceManager.addService("clipboard", new ClipboardService(context));
+ ServiceManager.addService(Context.CLIPBOARD_SERVICE,
+ new ClipboardService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Clipboard Service", e);
}
@@ -280,14 +290,16 @@
try {
Log.i(TAG, "Location Manager");
- ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context));
+ ServiceManager.addService(Context.LOCATION_SERVICE,
+ new LocationManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Location Manager", e);
}
try {
Log.i(TAG, "Search Service");
- ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) );
+ ServiceManager.addService(Context.SEARCH_SERVICE,
+ new SearchManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Search Service", e);
}
@@ -351,7 +363,8 @@
try {
Log.i(TAG, "Backup Service");
- ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
+ ServiceManager.addService(Context.BACKUP_SERVICE,
+ new BackupManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Backup Service", e);
}
@@ -391,6 +404,10 @@
// It is now time to start up the app processes...
+ if (devicePolicy != null) {
+ devicePolicy.systemReady();
+ }
+
if (notification != null) {
notification.systemReady();
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 40d194c..843058c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8381,7 +8381,7 @@
ActivityInfo ai = ris.get(i).activityInfo;
intent.setComponent(new ComponentName(ai.packageName, ai.name));
IIntentReceiver finisher = null;
- if (i == 0) {
+ if (i == ris.size()-1) {
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
@@ -8397,7 +8397,7 @@
Log.i(TAG, "Sending system update to: " + intent.getComponent());
broadcastIntentLocked(null, null, intent, null, finisher,
0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
- if (i == 0) {
+ if (finisher != null) {
mWaitingUpdate = true;
}
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java b/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
index 3b33a99..724ef6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
@@ -359,56 +359,58 @@
Time t = new Time(Time.TIMEZONE_UTC);
t.parse3339("1980-05-23");
- if (!t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23) {
+ if (!t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23) {
fail("Did not parse all-day date correctly");
}
t.parse3339("1980-05-23T09:50:50");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse timezone-offset-less date correctly");
}
t.parse3339("1980-05-23T09:50:50Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
t.parse3339("1980-05-23T09:50:50.0Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
t.parse3339("1980-05-23T09:50:50.12Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
t.parse3339("1980-05-23T09:50:50.123Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
- t.parse3339("1980-05-23T09:50:50-06:00");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
- t.hour != 9 || t.minute != 50 || t.second != 50 ||
- t.gmtoff != -6*3600) {
+ // The time should be normalized to UTC
+ t.parse3339("1980-05-23T09:50:50-01:05");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 10 || t.minute != 55 || t.second != 50 ||
+ t.gmtoff != 0) {
fail("Did not parse timezone-offset date correctly");
}
- t.parse3339("1980-05-23T09:50:50.123-06:00");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
- t.hour != 9 || t.minute != 50 || t.second != 50 ||
- t.gmtoff != -6*3600) {
+ // The time should be normalized to UTC
+ t.parse3339("1980-05-23T09:50:50.123-01:05");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 10 || t.minute != 55 || t.second != 50 ||
+ t.gmtoff != 0) {
fail("Did not parse timezone-offset date correctly");
}
diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
index 52286d1..31ee120 100644
--- a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
+++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
@@ -24,9 +24,8 @@
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.TransformGestureDetector;
+import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.LinearLayout;
@@ -48,9 +47,6 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- final LayoutInflater li = (LayoutInflater)getSystemService(
- LAYOUT_INFLATER_SERVICE);
this.setTitle(R.string.act_title);
LinearLayout root = new LinearLayout(this);
@@ -71,15 +67,19 @@
private float mPosY;
private float mScale = 1.f;
private Matrix mMatrix;
- private TransformGestureDetector mDetector;
+ private ScaleGestureDetector mDetector;
- private class Listener implements TransformGestureDetector.OnTransformGestureListener {
+ private float mLastX;
+ private float mLastY;
+
+ private class Listener implements ScaleGestureDetector.OnScaleGestureListener {
- public boolean onTransform(TransformGestureDetector detector) {
- Log.d("ttest", "Translation: (" + detector.getTranslateX() +
- ", " + detector.getTranslateY() + ")");
+ public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
+
Log.d("ttest", "Scale: " + scale);
+
+ // Limit the scale so our object doesn't get too big or disappear
if (mScale * scale > 0.1f) {
if (mScale * scale < 10.f) {
mScale *= scale;
@@ -89,16 +89,13 @@
} else {
mScale = 0.1f;
}
-
- mPosX += detector.getTranslateX();
- mPosY += detector.getTranslateY();
Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
float sizeX = mDrawable.getIntrinsicWidth()/2;
float sizeY = mDrawable.getIntrinsicHeight()/2;
- float centerX = detector.getCenterX();
- float centerY = detector.getCenterY();
+ float centerX = detector.getFocusX();
+ float centerY = detector.getFocusY();
float diffX = centerX - mPosX;
float diffY = centerY - mPosY;
diffX = diffX*scale - diffX;
@@ -115,24 +112,20 @@
return true;
}
- public boolean onTransformBegin(TransformGestureDetector detector) {
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
- public boolean onTransformEnd(TransformGestureDetector detector) {
- return true;
- }
-
- public boolean onTransformFling(TransformGestureDetector detector) {
- return false;
- }
-
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ mLastX = detector.getFocusX();
+ mLastY = detector.getFocusY();
+ }
}
public TransformView(Context context) {
super(context);
mMatrix = new Matrix();
- mDetector = new TransformGestureDetector(context, new Listener());
+ mDetector = new ScaleGestureDetector(context, new Listener());
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mPosX = metrics.widthPixels/2;
mPosY = metrics.heightPixels/2;
@@ -151,12 +144,37 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- boolean handled = mDetector.onTouchEvent(event);
+ mDetector.onTouchEvent(event);
- int pointerCount = event.getPointerCount();
- Log.d("ttest", "pointerCount: " + pointerCount);
+ // Handling single finger pan
+ if (!mDetector.isInProgress()) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float x = event.getX();
+ final float y = event.getY();
+ mPosX += x - mLastX;
+ mPosY += y - mLastY;
+ mLastX = x;
+ mLastY = y;
+
+ float sizeX = mDrawable.getIntrinsicWidth()/2;
+ float sizeY = mDrawable.getIntrinsicHeight()/2;
+
+ mMatrix.reset();
+ mMatrix.postTranslate(-sizeX, -sizeY);
+ mMatrix.postScale(mScale, mScale);
+ mMatrix.postTranslate(mPosX, mPosY);
+ invalidate();
+ break;
+ }
+ }
- return handled;
+ return true;
}
@Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
index ff1b295..35f022e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
@@ -20,6 +20,7 @@
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import javax.imageio.ImageIO;
@@ -33,6 +34,12 @@
mImage = ImageIO.read(input);
}
+ public Bitmap(InputStream is) throws IOException {
+ super(1, true, null, -1);
+
+ mImage = ImageIO.read(is);
+ }
+
Bitmap(BufferedImage image) {
super(1, true, null, -1);
mImage = image;
@@ -237,4 +244,35 @@
return createBitmap(colors, 0, width, width, height, config);
}
+ public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
+ int dstHeight, boolean filter) {
+ Matrix m;
+ synchronized (Bitmap.class) {
+ // small pool of just 1 matrix
+ m = sScaleMatrix;
+ sScaleMatrix = null;
+ }
+
+ if (m == null) {
+ m = new Matrix();
+ }
+
+ final int width = src.getWidth();
+ final int height = src.getHeight();
+ final float sx = dstWidth / (float)width;
+ final float sy = dstHeight / (float)height;
+ m.setScale(sx, sy);
+ Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
+
+ synchronized (Bitmap.class) {
+ // do we need to check for null? why not just assign everytime?
+ if (sScaleMatrix == null) {
+ sScaleMatrix = m;
+ }
+ }
+
+ return b;
+ }
+
+
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
new file mode 100644
index 0000000..e978fe8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -0,0 +1,566 @@
+/*
+ * 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 android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Creates Bitmap objects from various sources, including files, streams,
+ * and byte-arrays.
+ */
+public class BitmapFactory {
+ public static class Options {
+ /**
+ * Create a default Options object, which if left unchanged will give
+ * the same result from the decoder as if null were passed.
+ */
+ public Options() {
+ inDither = true;
+ inScaled = true;
+ }
+
+ /**
+ * If set to true, the decoder will return null (no bitmap), but
+ * the out... fields will still be set, allowing the caller to query
+ * the bitmap without having to allocate the memory for its pixels.
+ */
+ public boolean inJustDecodeBounds;
+
+ /**
+ * If set to a value > 1, requests the decoder to subsample the original
+ * image, returning a smaller image to save memory. The sample size is
+ * the number of pixels in either dimension that correspond to a single
+ * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
+ * an image that is 1/4 the width/height of the original, and 1/16 the
+ * number of pixels. Any value <= 1 is treated the same as 1. Note: the
+ * decoder will try to fulfill this request, but the resulting bitmap
+ * may have different dimensions that precisely what has been requested.
+ * Also, powers of 2 are often faster/easier for the decoder to honor.
+ */
+ public int inSampleSize;
+
+ /**
+ * If this is non-null, the decoder will try to decode into this
+ * internal configuration. If it is null, or the request cannot be met,
+ * the decoder will try to pick the best matching config based on the
+ * system's screen depth, and characteristics of the original image such
+ * as if it has per-pixel alpha (requiring a config that also does).
+ */
+ public Bitmap.Config inPreferredConfig;
+
+ /**
+ * If dither is true, the decoder will attempt to dither the decoded
+ * image.
+ */
+ public boolean inDither;
+
+ /**
+ * The pixel density to use for the bitmap. This will always result
+ * in the returned bitmap having a density set for it (see
+ * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)). In addition,
+ * if {@link #inScaled} is set (which it is by default} and this
+ * density does not match {@link #inTargetDensity}, then the bitmap
+ * will be scaled to the target density before being returned.
+ *
+ * <p>If this is 0,
+ * {@link BitmapFactory#decodeResource(Resources, int)},
+ * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+ * and {@link BitmapFactory#decodeResourceStream}
+ * will fill in the density associated with the resource. The other
+ * functions will leave it as-is and no density will be applied.
+ *
+ * @see #inTargetDensity
+ * @see #inScreenDensity
+ * @see #inScaled
+ * @see Bitmap#setDensity(int)
+ * @see android.util.DisplayMetrics#densityDpi
+ */
+ public int inDensity;
+
+ /**
+ * The pixel density of the destination this bitmap will be drawn to.
+ * This is used in conjunction with {@link #inDensity} and
+ * {@link #inScaled} to determine if and how to scale the bitmap before
+ * returning it.
+ *
+ * <p>If this is 0,
+ * {@link BitmapFactory#decodeResource(Resources, int)},
+ * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+ * and {@link BitmapFactory#decodeResourceStream}
+ * will fill in the density associated the Resources object's
+ * DisplayMetrics. The other
+ * functions will leave it as-is and no scaling for density will be
+ * performed.
+ *
+ * @see #inDensity
+ * @see #inScreenDensity
+ * @see #inScaled
+ * @see android.util.DisplayMetrics#densityDpi
+ */
+ public int inTargetDensity;
+
+ /**
+ * The pixel density of the actual screen that is being used. This is
+ * purely for applications running in density compatibility code, where
+ * {@link #inTargetDensity} is actually the density the application
+ * sees rather than the real screen density.
+ *
+ * <p>By setting this, you
+ * allow the loading code to avoid scaling a bitmap that is currently
+ * in the screen density up/down to the compatibility density. Instead,
+ * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
+ * bitmap will be left as-is. Anything using the resulting bitmap
+ * must also used {@link Bitmap#getScaledWidth(int)
+ * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
+ * Bitmap.getScaledHeight} to account for any different between the
+ * bitmap's density and the target's density.
+ *
+ * <p>This is never set automatically for the caller by
+ * {@link BitmapFactory} itself. It must be explicitly set, since the
+ * caller must deal with the resulting bitmap in a density-aware way.
+ *
+ * @see #inDensity
+ * @see #inTargetDensity
+ * @see #inScaled
+ * @see android.util.DisplayMetrics#densityDpi
+ */
+ public int inScreenDensity;
+
+ /**
+ * When this flag is set, if {@link #inDensity} and
+ * {@link #inTargetDensity} are not 0, the
+ * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
+ * rather than relying on the graphics system scaling it each time it
+ * is drawn to a Canvas.
+ *
+ * <p>This flag is turned on by default and should be turned off if you need
+ * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this
+ * flag and are always scaled.
+ */
+ public boolean inScaled;
+
+ /**
+ * If this is set to true, then the resulting bitmap will allocate its
+ * pixels such that they can be purged if the system needs to reclaim
+ * memory. In that instance, when the pixels need to be accessed again
+ * (e.g. the bitmap is drawn, getPixels() is called), they will be
+ * automatically re-decoded.
+ *
+ * For the re-decode to happen, the bitmap must have access to the
+ * encoded data, either by sharing a reference to the input
+ * or by making a copy of it. This distinction is controlled by
+ * inInputShareable. If this is true, then the bitmap may keep a shallow
+ * reference to the input. If this is false, then the bitmap will
+ * explicitly make a copy of the input data, and keep that. Even if
+ * sharing is allowed, the implementation may still decide to make a
+ * deep copy of the input data.
+ */
+ public boolean inPurgeable;
+
+ /**
+ * This field works in conjuction with inPurgeable. If inPurgeable is
+ * false, then this field is ignored. If inPurgeable is true, then this
+ * field determines whether the bitmap can share a reference to the
+ * input data (inputstream, array, etc.) or if it must make a deep copy.
+ */
+ public boolean inInputShareable;
+
+ /**
+ * Normally bitmap allocations count against the dalvik heap, which
+ * means they help trigger GCs when a lot have been allocated. However,
+ * in rare cases, the caller may want to allocate the bitmap outside of
+ * that heap. To request that, set inNativeAlloc to true. In these
+ * rare instances, it is solely up to the caller to ensure that OOM is
+ * managed explicitly by calling bitmap.recycle() as soon as such a
+ * bitmap is no longer needed.
+ *
+ * @hide pending API council approval
+ */
+ public boolean inNativeAlloc;
+
+ /**
+ * The resulting width of the bitmap, set independent of the state of
+ * inJustDecodeBounds. However, if there is an error trying to decode,
+ * outWidth will be set to -1.
+ */
+ public int outWidth;
+
+ /**
+ * The resulting height of the bitmap, set independent of the state of
+ * inJustDecodeBounds. However, if there is an error trying to decode,
+ * outHeight will be set to -1.
+ */
+ public int outHeight;
+
+ /**
+ * If known, this string is set to the mimetype of the decoded image.
+ * If not know, or there is an error, it is set to null.
+ */
+ public String outMimeType;
+
+ /**
+ * Temp storage to use for decoding. Suggest 16K or so.
+ */
+ public byte[] inTempStorage;
+
+ private native void requestCancel();
+
+ /**
+ * Flag to indicate that cancel has been called on this object. This
+ * is useful if there's an intermediary that wants to first decode the
+ * bounds and then decode the image. In that case the intermediary
+ * can check, inbetween the bounds decode and the image decode, to see
+ * if the operation is canceled.
+ */
+ public boolean mCancel;
+
+ /**
+ * This can be called from another thread while this options object is
+ * inside a decode... call. Calling this will notify the decoder that
+ * it should cancel its operation. This is not guaranteed to cancel
+ * the decode, but if it does, the decoder... operation will return
+ * null, or if inJustDecodeBounds is true, will set outWidth/outHeight
+ * to -1
+ */
+ public void requestCancelDecode() {
+ mCancel = true;
+ requestCancel();
+ }
+ }
+
+ /**
+ * Decode a file path into a bitmap. If the specified file name is null,
+ * or cannot be decoded into a bitmap, the function returns null.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeFile(String pathName, Options opts) {
+ Bitmap bm = null;
+ InputStream stream = null;
+ try {
+ stream = new FileInputStream(pathName);
+ bm = decodeStream(stream, null, opts);
+ } catch (Exception e) {
+ /* do nothing.
+ If the exception happened on open, bm will be null.
+ */
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // do nothing here
+ }
+ }
+ }
+ return bm;
+ }
+
+ /**
+ * Decode a file path into a bitmap. If the specified file name is null,
+ * or cannot be decoded into a bitmap, the function returns null.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @return the resulting decoded bitmap, or null if it could not be decoded.
+ */
+ public static Bitmap decodeFile(String pathName) {
+ return decodeFile(pathName, null);
+ }
+
+ /**
+ * Decode a new Bitmap from an InputStream. This InputStream was obtained from
+ * resources, which we pass to be able to scale the bitmap accordingly.
+ */
+ public static Bitmap decodeResourceStream(Resources res, TypedValue value,
+ InputStream is, Rect pad, Options opts) {
+
+ if (opts == null) {
+ opts = new Options();
+ }
+
+ if (opts.inDensity == 0 && value != null) {
+ final int density = value.density;
+ if (density == TypedValue.DENSITY_DEFAULT) {
+ opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (density != TypedValue.DENSITY_NONE) {
+ opts.inDensity = density;
+ }
+ }
+
+ if (opts.inTargetDensity == 0 && res != null) {
+ opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
+ }
+
+ return decodeStream(is, pad, opts);
+ }
+
+ /**
+ * Synonym for opening the given resource and calling
+ * {@link #decodeResourceStream}.
+ *
+ * @param res The resources object containing the image data
+ * @param id The resource id of the image data
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeResource(Resources res, int id, Options opts) {
+ Bitmap bm = null;
+ InputStream is = null;
+
+ try {
+ final TypedValue value = new TypedValue();
+ is = res.openRawResource(id, value);
+
+ bm = decodeResourceStream(res, value, is, null, opts);
+ } catch (Exception e) {
+ /* do nothing.
+ If the exception happened on open, bm will be null.
+ If it happened on close, bm is still valid.
+ */
+ } finally {
+ try {
+ if (is != null) is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ return bm;
+ }
+
+ /**
+ * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
+ * will null Options.
+ *
+ * @param res The resources object containing the image data
+ * @param id The resource id of the image data
+ * @return The decoded bitmap, or null if the image could not be decode.
+ */
+ public static Bitmap decodeResource(Resources res, int id) {
+ return decodeResource(res, id, null);
+ }
+
+ /**
+ * Decode an immutable bitmap from the specified byte array.
+ *
+ * @param data byte array of compressed image data
+ * @param offset offset into imageData for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
+ if ((offset | length) < 0 || data.length < offset + length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge.
+ return null;
+ //return nativeDecodeByteArray(data, offset, length, opts);
+ }
+
+ /**
+ * Decode an immutable bitmap from the specified byte array.
+ *
+ * @param data byte array of compressed image data
+ * @param offset offset into imageData for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @return The decoded bitmap, or null if the image could not be decode.
+ */
+ public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
+ return decodeByteArray(data, offset, length, null);
+ }
+
+ /**
+ * Decode an input stream into a bitmap. If the input stream is null, or
+ * cannot be used to decode a bitmap, the function returns null.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * bitmap.
+ * @param outPadding If not null, return the padding rect for the bitmap if
+ * it exists, otherwise set padding to [-1,-1,-1,-1]. If
+ * no bitmap is returned (null) then padding is
+ * unchanged.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+ // we don't throw in this case, thus allowing the caller to only check
+ // the cache, and not force the image to be decoded.
+ if (is == null) {
+ return null;
+ }
+
+ // we need mark/reset to work properly
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is, 16 * 1024);
+ }
+
+ // so we can call reset() if a given codec gives up after reading up to
+ // this many bytes. FIXME: need to find out from the codecs what this
+ // value should be.
+ is.mark(1024);
+
+ Bitmap bm;
+
+ if (is instanceof AssetManager.AssetInputStream) {
+ // FIXME: log this.
+ return null;
+ } else {
+ // pass some temp storage down to the native code. 1024 is made up,
+ // but should be large enough to avoid too many small calls back
+ // into is.read(...) This number is not related to the value passed
+ // to mark(...) above.
+ try {
+ bm = new Bitmap(is);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ return finishDecode(bm, outPadding, opts);
+ }
+
+ private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
+ if (bm == null || opts == null) {
+ return bm;
+ }
+
+ final int density = opts.inDensity;
+ if (density == 0) {
+ return bm;
+ }
+
+ bm.setDensity(density);
+ final int targetDensity = opts.inTargetDensity;
+ if (targetDensity == 0 || density == targetDensity
+ || density == opts.inScreenDensity) {
+ return bm;
+ }
+
+ byte[] np = bm.getNinePatchChunk();
+ final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np);
+ if (opts.inScaled || isNinePatch) {
+ float scale = targetDensity / (float)density;
+ // TODO: This is very inefficient and should be done in native by Skia
+ final Bitmap oldBitmap = bm;
+ bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+ (int) (bm.getHeight() * scale + 0.5f), true);
+ oldBitmap.recycle();
+
+ if (isNinePatch) {
+ //np = nativeScaleNinePatch(np, scale, outPadding);
+ bm.setNinePatchChunk(np);
+ }
+ bm.setDensity(targetDensity);
+ }
+
+ return bm;
+ }
+
+ /**
+ * Decode an input stream into a bitmap. If the input stream is null, or
+ * cannot be used to decode a bitmap, the function returns null.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * bitmap.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeStream(InputStream is) {
+ return decodeStream(is, null, null);
+ }
+
+ /**
+ * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+ * return null. The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as-is.
+ *
+ * @param fd The file descriptor containing the bitmap data to decode
+ * @param outPadding If not null, return the padding rect for the bitmap if
+ * it exists, otherwise set padding to [-1,-1,-1,-1]. If
+ * no bitmap is returned (null) then padding is
+ * unchanged.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return the decoded bitmap, or null
+ */
+ public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+ return null;
+
+ /* FIXME: implement as needed
+ try {
+ if (MemoryFile.isMemoryFile(fd)) {
+ int mappedlength = MemoryFile.getMappedSize(fd);
+ MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+ InputStream is = file.getInputStream();
+ Bitmap bm = decodeStream(is, outPadding, opts);
+ return finishDecode(bm, outPadding, opts);
+ }
+ } catch (IOException ex) {
+ // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
+ return null;
+ }
+ //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+ //return finishDecode(bm, outPadding, opts);
+ */
+ }
+
+ /**
+ * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+ * return null. The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as is.
+ *
+ * @param fd The file descriptor containing the bitmap data to decode
+ * @return the decoded bitmap, or null
+ */
+ public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
+ return decodeFileDescriptor(fd, null, null);
+ }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 4986c77..02e3220 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -236,10 +236,15 @@
*/
@Override
public int save() {
+ // get the current save count
+ int count = mGraphicsStack.size();
+
+ // create a new graphics and add it to the stack
Graphics2D g = (Graphics2D)getGraphics2d().create();
mGraphicsStack.push(g);
- return mGraphicsStack.size() - 1;
+ // return the old save count
+ return count;
}
/* (non-Javadoc)
@@ -274,10 +279,9 @@
*/
@Override
public int getSaveCount() {
- return mGraphicsStack.size() - 1;
+ return mGraphicsStack.size();
}
-
/* (non-Javadoc)
* @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
*/
@@ -953,10 +957,6 @@
*/
@Override
public void setMatrix(Matrix matrix) {
- // since SetMatrix *replaces* all the other transformation, we have to restore/save
- restore();
- save();
-
// get the new current graphics
Graphics2D g = getGraphics2d();
@@ -968,6 +968,27 @@
}
}
+ /* (non-Javadoc)
+ * @see android.graphics.Canvas#concat(android.graphics.Matrix)
+ */
+ @Override
+ public void concat(Matrix matrix) {
+ // get the current top graphics2D object.
+ Graphics2D g = getGraphics2d();
+
+ // get its current matrix
+ AffineTransform currentTx = g.getTransform();
+ // get the AffineTransform of the given matrix
+ AffineTransform matrixTx = matrix.getTransform();
+
+ // combine them so that the given matrix is applied after.
+ currentTx.preConcatenate(matrixTx);
+
+ // give it to the graphics2D as a new matrix replacing all previous transform
+ g.setTransform(currentTx);
+ }
+
+
// --------------------
/* (non-Javadoc)
@@ -1008,15 +1029,6 @@
}
/* (non-Javadoc)
- * @see android.graphics.Canvas#concat(android.graphics.Matrix)
- */
- @Override
- public void concat(Matrix matrix) {
- // TODO Auto-generated method stub
- super.concat(matrix);
- }
-
- /* (non-Javadoc)
* @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
*/
@Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
index 3974e08..522415c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
@@ -87,8 +87,12 @@
}
public AffineTransform getTransform() {
- return new AffineTransform(mValues[0], mValues[1], mValues[2],
- mValues[3], mValues[4], mValues[5]);
+ // the AffineTransform constructor takes the value in a different order
+ // for a matrix [ 0 1 2 ]
+ // [ 3 4 5 ]
+ // the order is 0, 3, 1, 4, 2, 5...
+ return new AffineTransform(mValues[0], mValues[3], mValues[1],
+ mValues[4], mValues[2], mValues[5]);
}
public boolean hasPerspective() {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 5a13b0b..2623570 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -43,6 +43,7 @@
public final static String[] RENAMED_CLASSES =
new String[] {
"android.graphics.Bitmap", "android.graphics._Original_Bitmap",
+ "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory",
"android.graphics.BitmapShader", "android.graphics._Original_BitmapShader",
"android.graphics.Canvas", "android.graphics._Original_Canvas",
"android.graphics.ComposeShader", "android.graphics._Original_ComposeShader",