Merge change 5600
* changes:
Rework the property parsing code.
diff --git a/Android.mk b/Android.mk
index 58b149e..01aef7a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,6 +85,7 @@
core/java/android/app/IWallpaperService.aidl \
core/java/android/app/IWallpaperServiceCallback.aidl \
core/java/android/backup/IBackupManager.aidl \
+ core/java/android/backup/IRestoreObserver.aidl \
core/java/android/backup/IRestoreSession.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothDevice.aidl \
diff --git a/api/current.xml b/api/current.xml
index 8b5f7ed..1e79de5 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -17313,6 +17313,17 @@
visibility="public"
>
</field>
+<field name="ERROR_CODE_BAD_REQUEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ERROR_CODE_CANCELED"
type="int"
transient="false"
@@ -24301,6 +24312,19 @@
<parameter name="position" type="int">
</parameter>
</method>
+<method name="itemForPosition"
+ return="android.app.LauncherActivity.ListItem"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="makeListItems"
return="java.util.List<android.app.LauncherActivity.ListItem>"
abstract="false"
@@ -24409,6 +24433,16 @@
visibility="public"
>
</field>
+<field name="resolveInfo"
+ type="android.content.pm.ResolveInfo"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ListActivity"
extends="android.app.Activity"
@@ -184646,7 +184680,7 @@
<method name="startMethodTracing"
return="void"
abstract="false"
- native="true"
+ native="false"
synchronized="false"
static="true"
final="false"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 2f4a1c9..d8ca525 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -98,7 +98,7 @@
LOGD("CameraService::connect E (pid %d, client %p)", callingPid,
cameraClient->asBinder().get());
- Mutex::Autolock lock(mLock);
+ Mutex::Autolock lock(mServiceLock);
sp<Client> client;
if (mClient != 0) {
sp<Client> currentClient = mClient.promote();
@@ -125,13 +125,14 @@
LOGD("New client (pid %d) connecting, old reference was dangling...",
callingPid);
mClient.clear();
- if (mUsers > 0) {
- LOGD("Still have client, rejected");
- return client;
- }
}
}
+ if (mUsers > 0) {
+ LOGD("Still have client, rejected");
+ return client;
+ }
+
// create a new Client object
client = new Client(this, cameraClient, callingPid);
mClient = client;
@@ -152,7 +153,7 @@
// destructor won't be called with the lock held.
sp<Client> client;
- Mutex::Autolock lock(mLock);
+ Mutex::Autolock lock(mServiceLock);
if (mClient == 0) {
// This happens when we have already disconnected.
@@ -390,8 +391,6 @@
// from the user directly, or called by the destructor.
if (mHardware == 0) return;
- mCameraService->removeClient(mCameraClient);
-
LOGD("hardware teardown");
// Before destroying mHardware, we must make sure it's in the
// idle state.
@@ -402,6 +401,7 @@
mHardware->release();
mHardware.clear();
+ mCameraService->removeClient(mCameraClient);
mCameraService->decUsers();
LOGD("Client::disconnect() X (pid %d)", callingPid);
@@ -661,7 +661,7 @@
sp<Client> client = 0;
CameraService *service = static_cast<CameraService*>(user);
if (service != NULL) {
- Mutex::Autolock ourLock(service->mLock);
+ Mutex::Autolock ourLock(service->mServiceLock);
if (service->mClient != 0) {
client = service->mClient.promote();
if (client == 0) {
@@ -1104,7 +1104,7 @@
result.append(buffer);
write(fd, result.string(), result.size());
} else {
- AutoMutex lock(&mLock);
+ AutoMutex lock(&mServiceLock);
if (mClient != 0) {
sp<Client> currentClient = mClient.promote();
sprintf(buffer, "Client (%p) PID: %d\n",
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 729e539..8b8b54c 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -199,7 +199,7 @@
virtual void incUsers();
virtual void decUsers();
- mutable Mutex mLock;
+ mutable Mutex mServiceLock;
wp<Client> mClient;
#if DEBUG_HEAP_LEAKS
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 841e3df..01083f1 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
package com.android.commands.bmgr;
import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
import android.backup.IRestoreSession;
import android.backup.RestoreSet;
import android.os.RemoteException;
@@ -180,6 +181,25 @@
}
}
+ class RestoreObserver extends IRestoreObserver.Stub {
+ boolean done;
+ public void restoreStarting(int numPackages) {
+ System.out.println("restoreStarting: " + numPackages + " packages");
+ }
+
+ public void onUpdate(int nowBeingRestored) {
+ System.out.println("onUpdate: " + nowBeingRestored);
+ }
+
+ public void restoreFinished(int error) {
+ System.out.println("restoreFinished: " + error);
+ synchronized (this) {
+ done = true;
+ this.notify();
+ }
+ }
+ }
+
private void doRestore() {
int token;
try {
@@ -189,6 +209,8 @@
return;
}
+ RestoreObserver observer = new RestoreObserver();
+
try {
int curTransport = mBmgr.getCurrentTransport();
mRestore = mBmgr.beginRestoreSession(curTransport);
@@ -200,7 +222,7 @@
for (RestoreSet s : sets) {
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
- mRestore.performRestore(token);
+ mRestore.performRestore(token, observer);
break;
}
}
@@ -209,6 +231,17 @@
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
+
+ // now wait for it to be done
+ synchronized (observer) {
+ while (!observer.done) {
+ try {
+ observer.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ System.out.println("done");
}
private String nextArg() {
@@ -229,4 +262,4 @@
System.err.println(" bmgr restore token#");
System.err.println(" bmgr run");
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java
index b383c61..fde920e 100644
--- a/core/java/android/accounts/Constants.java
+++ b/core/java/android/accounts/Constants.java
@@ -25,6 +25,7 @@
public static final int ERROR_CODE_INVALID_RESPONSE = 5;
public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
+ public static final int ERROR_CODE_BAD_REQUEST = 8;
public static final String ACCOUNTS_KEY = "accounts";
public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 79588ea..62dc651 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3697,6 +3697,13 @@
*/
Locale.setDefault(data.config.locale);
+ /*
+ * Update the system configuration since its preloaded and might not
+ * reflect configuration changes. The configuration object passed
+ * in AppBindData can be safely assumed to be up to date
+ */
+ Resources.getSystem().updateConfiguration(mConfiguration, null);
+
data.info = getPackageInfoNoCheck(data.appInfo);
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index e810775..0ac8a1e 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -67,7 +67,7 @@
* here after writing the requested data to dataFd.
*/
public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState);
+ ParcelFileDescriptor newState) throws IOException;
/**
* The application is being restored from backup, and should replace any
@@ -120,6 +120,9 @@
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
try {
BackupAgent.this.onBackup(oldState, output, newState);
+ } catch (IOException ex) {
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
} catch (RuntimeException ex) {
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 8d249da..accdda9 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -60,26 +60,20 @@
* An item in the list
*/
public static class ListItem {
+ public ResolveInfo resolveInfo;
public CharSequence label;
- //public CharSequence description;
public Drawable icon;
public String packageName;
public String className;
public Bundle extras;
ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
+ this.resolveInfo = resolveInfo;
label = resolveInfo.loadLabel(pm);
if (label == null && resolveInfo.activityInfo != null) {
label = resolveInfo.activityInfo.name;
}
- /*
- if (resolveInfo.activityInfo != null &&
- resolveInfo.activityInfo.applicationInfo != null) {
- description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm);
- }
- */
-
icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
packageName = resolveInfo.activityInfo.applicationInfo.packageName;
className = resolveInfo.activityInfo.name;
@@ -122,6 +116,14 @@
return intent;
}
+ public ListItem itemForPosition(int position) {
+ if (mActivitiesList == null) {
+ return null;
+ }
+
+ return mActivitiesList.get(position);
+ }
+
public int getCount() {
return mActivitiesList != null ? mActivitiesList.size() : 0;
}
@@ -354,6 +356,16 @@
}
/**
+ * Return the {@link ListItem} for a specific position in our
+ * {@link android.widget.ListView}.
+ * @param position The item to return
+ */
+ protected ListItem itemForPosition(int position) {
+ ActivityAdapter adapter = (ActivityAdapter) mAdapter;
+ return adapter.itemForPosition(position);
+ }
+
+ /**
* Get the base intent to use when running
* {@link PackageManager#queryIntentActivities(Intent, int)}.
*/
diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java
new file mode 100644
index 0000000..ab24675
--- /dev/null
+++ b/core/java/android/backup/AbsoluteFileBackupHelper.java
@@ -0,0 +1,66 @@
+/*
+ * 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.backup;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+
+/**
+ * Like FileBackupHelper, but takes absolute paths for the files instead of
+ * subpaths of getFilesDir()
+ *
+ * @hide
+ */
+public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper {
+ private static final String TAG = "AbsoluteFileBackupHelper";
+
+ Context mContext;
+ String[] mFiles;
+
+ public AbsoluteFileBackupHelper(Context context, String... files) {
+ super(context);
+
+ mContext = context;
+ mFiles = files;
+ }
+
+ /**
+ * Based on oldState, determine which of the files from the application's data directory
+ * need to be backed up, write them to the data stream, and fill in newState with the
+ * state as it exists now.
+ */
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+ // use the file paths as the keys, too
+ performBackup_checked(oldState, data, newState, mFiles, mFiles);
+ }
+
+ public void restoreEntity(BackupDataInputStream data) {
+ // TODO: turn this off before ship
+ Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size());
+ String key = data.getKey();
+ if (isKeyInList(key, mFiles)) {
+ File f = new File(key);
+ writeFile(f, data);
+ }
+ }
+}
+
diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java
index 3720d50..5d0c4a2 100644
--- a/core/java/android/backup/BackupHelperAgent.java
+++ b/core/java/android/backup/BackupHelperAgent.java
@@ -34,7 +34,7 @@
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
+ ParcelFileDescriptor newState) throws IOException {
mDispatcher.performBackup(oldState, data, newState);
}
diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java
index b25c3e3..6ccb83e 100644
--- a/core/java/android/backup/BackupHelperDispatcher.java
+++ b/core/java/android/backup/BackupHelperDispatcher.java
@@ -19,7 +19,10 @@
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.IOException;
+import java.io.FileDescriptor;
import java.util.TreeMap;
import java.util.Map;
@@ -27,6 +30,11 @@
public class BackupHelperDispatcher {
private static final String TAG = "BackupHelperDispatcher";
+ private static class Header {
+ int chunkSize; // not including the header
+ String keyPrefix;
+ }
+
TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
public BackupHelperDispatcher() {
@@ -36,13 +44,63 @@
mHelpers.put(keyPrefix, helper);
}
- /** TODO: Make this save and restore the key prefix. */
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
- // Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
- for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) {
- data.setKeyPrefix(entry.getKey());
- entry.getValue().performBackup(oldState, data, newState);
+ ParcelFileDescriptor newState) throws IOException {
+ // First, do the helpers that we've already done, since they're already in the state
+ // file.
+ int err;
+ Header header = new Header();
+ TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
+ FileDescriptor oldStateFD = null;
+ FileDescriptor newStateFD = newState.getFileDescriptor();
+
+ if (oldState != null) {
+ oldStateFD = oldState.getFileDescriptor();
+ while ((err = readHeader_native(header, oldStateFD)) >= 0) {
+ if (err == 0) {
+ BackupHelper helper = helpers.get(header.keyPrefix);
+ Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
+ if (helper != null) {
+ doOneBackup(oldState, data, newState, header, helper);
+ helpers.remove(header.keyPrefix);
+ } else {
+ skipChunk_native(oldStateFD, header.chunkSize);
+ }
+ }
+ }
+ }
+
+ // Then go through and do the rest that we haven't done.
+ for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
+ header.keyPrefix = entry.getKey();
+ Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
+ BackupHelper helper = entry.getValue();
+ doOneBackup(oldState, data, newState, header, helper);
+ }
+ }
+
+ private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState, Header header, BackupHelper helper)
+ throws IOException {
+ int err;
+ FileDescriptor newStateFD = newState.getFileDescriptor();
+
+ // allocate space for the header in the file
+ int pos = allocateHeader_native(header, newStateFD);
+ if (pos < 0) {
+ throw new IOException("allocateHeader_native failed (error " + pos + ")");
+ }
+
+ data.setKeyPrefix(header.keyPrefix);
+
+ // do the backup
+ helper.performBackup(oldState, data, newState);
+
+ // fill in the header (seeking back to pos). The file pointer will be returned to
+ // where it was at the end of performBackup. Header.chunkSize will not be filled in.
+ err = writeHeader_native(header, newStateFD, pos);
+ if (err != 0) {
+ throw new IOException("writeHeader_native failed (error " + err + ")");
}
}
@@ -83,5 +141,11 @@
helper.writeRestoreSnapshot(newState);
}
}
+
+ private static native int readHeader_native(Header h, FileDescriptor fd);
+ private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
+
+ private static native int allocateHeader_native(Header h, FileDescriptor fd);
+ private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
}
diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl
new file mode 100644
index 0000000..59e59fc
--- /dev/null
+++ b/core/java/android/backup/IRestoreObserver.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.backup;
+
+/**
+ * Callback class for receiving progress reports during a restore operation.
+ *
+ * @hide
+ */
+interface IRestoreObserver {
+ /**
+ * The restore operation has begun.
+ *
+ * @param numPackages The total number of packages being processed in
+ * this restore operation.
+ */
+ void restoreStarting(int numPackages);
+
+ /**
+ * An indication of which package is being restored currently, out of the
+ * total number provided in the restoreStarting() callback. This method
+ * is not guaranteed to be called.
+ *
+ * @param nowBeingRestored The index, between 1 and the numPackages parameter
+ * to the restoreStarting() callback, of the package now being restored.
+ */
+ void onUpdate(int nowBeingRestored);
+
+ /**
+ * The restore operation has completed.
+ *
+ * @param error Zero on success; a nonzero error code if the restore operation
+ * as a whole failed.
+ */
+ void restoreFinished(int error);
+}
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index 6bca865..ac01c2d 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -17,6 +17,7 @@
package android.backup;
import android.backup.RestoreSet;
+import android.backup.IRestoreObserver;
/**
* Binder interface used by clients who wish to manage a restore operation. Every
@@ -41,8 +42,10 @@
*
* @param token The token from {@link getAvailableRestoreSets()} corresponding to
* the restore set that should be used.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
*/
- int performRestore(int token);
+ int performRestore(int token, IRestoreObserver observer);
/**
* End this restore session. After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java
index 96a99ae..eeca148 100644
--- a/core/java/android/backup/RestoreSet.java
+++ b/core/java/android/backup/RestoreSet.java
@@ -43,14 +43,14 @@
* transport. This is guaranteed to be valid for the duration of a restore
* session, but is meaningless once the session has ended.
*/
- public int token;
+ public long token;
public RestoreSet() {
// Leave everything zero / null
}
- public RestoreSet(String _name, String _dev, int _token) {
+ public RestoreSet(String _name, String _dev, long _token) {
name = _name;
device = _dev;
token = _token;
@@ -65,7 +65,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeString(device);
- out.writeInt(token);
+ out.writeLong(token);
}
public static final Parcelable.Creator<RestoreSet> CREATOR
@@ -82,6 +82,6 @@
private RestoreSet(Parcel in) {
name = in.readString();
device = in.readString();
- token = in.readInt();
+ token = in.readLong();
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 33e769e..87f762d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1593,6 +1593,15 @@
public static final String ACTION_REMOTE_INTENT =
"android.intent.action.REMOTE_INTENT";
+ /**
+ * @hide
+ * TODO: This will be unhidden in a later CL.
+ * Broadcast Action: The TextToSpeech synthesizer has completed processing
+ * all of the text in the speech queue.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
+ "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 680fef8..179b9bd 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -17,8 +17,15 @@
package android.content.res;
import android.content.pm.ApplicationInfo;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
/**
* CompatibilityInfo class keeps the information about compatibility mode that the application is
@@ -27,6 +34,9 @@
* {@hide}
*/
public class CompatibilityInfo {
+ private static final boolean DBG = false;
+ private static final String TAG = "CompatibilityInfo";
+
/** default compatibility info object for compatible applications */
public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo();
@@ -41,36 +51,75 @@
public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
/**
+ * The x-shift mode that controls the position of the content or the window under
+ * compatibility mode.
+ * {@see getTranslator}
+ * {@see Translator#mShiftMode}
+ */
+ private static final int X_SHIFT_NONE = 0;
+ private static final int X_SHIFT_CONTENT = 1;
+ private static final int X_SHIFT_AND_CLIP_CONTENT = 2;
+ private static final int X_SHIFT_WINDOW = 3;
+
+
+ /**
+ * A compatibility flags
+ */
+ private int compatibilityFlags;
+
+ /**
+ * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
+ * {@see compatibilityFlag}
+ */
+ private static final int SCALING_REQUIRED = 1;
+
+ /**
+ * A flag mask to indicates that the application can expand over the original size.
+ * The flag is set to true if
+ * 1) Application declares its expandable in manifest file using <expandable /> or
+ * 2) The screen size is same as (320 x 480) * density.
+ * {@see compatibilityFlag}
+ */
+ private static final int EXPANDABLE = 2;
+
+ /**
+ * A flag mask to tell if the application is configured to be expandable. This differs
+ * from EXPANDABLE in that the application that is not expandable will be
+ * marked as expandable if it runs in (320x 480) * density screen size.
+ */
+ private static final int CONFIGURED_EXPANDABLE = 4;
+
+ private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE;
+
+ /**
* Application's scale.
*/
- public final float mApplicationScale;
+ public final float applicationScale;
/**
* Application's inverted scale.
*/
- public final float mApplicationInvertedScale;
-
- /**
- * A boolean flag to indicates that the application can expand over the original size.
- * The flag is set to true if
- * 1) Application declares its expandable in manifest file using <expandable /> or
- * 2) The screen size is same as (320 x 480) * density.
- */
- public boolean mExpandable;
+ public final float applicationInvertedScale;
+
/**
- * A expandable flag in the configuration.
+ * Window size in Compatibility Mode, in real pixels. This is updated by
+ * {@link DisplayMetrics#updateMetrics}.
*/
- public final boolean mConfiguredExpandable;
+ private int mWidth;
+ private int mHeight;
/**
- * A boolean flag to tell if the application needs scaling (when mApplicationScale != 1.0f)
+ * The x offset to center the window content. In X_SHIFT_WINDOW mode, the offset is added
+ * to the window's layout. In X_SHIFT_CONTENT/X_SHIFT_AND_CLIP_CONTENT mode, the offset
+ * is used to translate the Canvas.
*/
- public final boolean mScalingRequired;
+ private int mXOffset;
public CompatibilityInfo(ApplicationInfo appInfo) {
- mExpandable = mConfiguredExpandable =
- (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0;
+ if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+ compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+ }
float packageDensityScale = -1.0f;
if (appInfo.supportsDensities != null) {
@@ -93,23 +142,323 @@
}
}
if (packageDensityScale > 0.0f) {
- mApplicationScale = packageDensityScale;
+ applicationScale = packageDensityScale;
} else {
- mApplicationScale = DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
+ applicationScale =
+ DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
}
- mApplicationInvertedScale = 1.0f / mApplicationScale;
- mScalingRequired = mApplicationScale != 1.0f;
+ applicationInvertedScale = 1.0f / applicationScale;
+ if (applicationScale != 1.0f) {
+ compatibilityFlags |= SCALING_REQUIRED;
+ }
}
private CompatibilityInfo() {
- mApplicationScale = mApplicationInvertedScale = 1.0f;
- mExpandable = mConfiguredExpandable = true;
- mScalingRequired = false;
+ applicationScale = applicationInvertedScale = 1.0f;
+ compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
}
+ /**
+ * Sets the application's visible rect in compatibility mode.
+ * @param xOffset the application's x offset that is added to center the content.
+ * @param widthPixels the application's width in real pixels on the screen.
+ * @param heightPixels the application's height in real pixels on the screen.
+ */
+ public void setVisibleRect(int xOffset, int widthPixels, int heightPixels) {
+ this.mXOffset = xOffset;
+ mWidth = widthPixels;
+ mHeight = heightPixels;
+ }
+
+ /**
+ * Sets expandable bit in the compatibility flag.
+ */
+ public void setExpandable(boolean expandable) {
+ if (expandable) {
+ compatibilityFlags |= CompatibilityInfo.EXPANDABLE;
+ } else {
+ compatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
+ }
+ }
+
+ /**
+ * @return true if the application is configured to be expandable.
+ */
+ public boolean isConfiguredExpandable() {
+ return (compatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
+ }
+
+ /**
+ * @return true if the scaling is required
+ */
+ public boolean isScalingRequired() {
+ return (compatibilityFlags & SCALING_REQUIRED) != 0;
+ }
+
@Override
public String toString() {
- return "CompatibilityInfo{scale=" + mApplicationScale +
- ", expandable=" + mExpandable + "}";
+ return "CompatibilityInfo{scale=" + applicationScale +
+ ", compatibility flag=" + compatibilityFlags + "}";
}
-}
+
+ /**
+ * Returns the translator which can translate the coordinates of the window.
+ * There are five different types of Translator.
+ *
+ * 1) {@link CompatibilityInfo#X_SHIFT_AND_CLIP_CONTENT}
+ * Shift and clip the content of the window at drawing time. Used for activities'
+ * main window (with no gravity).
+ * 2) {@link CompatibilityInfo#X_SHIFT_CONTENT}
+ * Shift the content of the window at drawing time. Used for windows that is created by
+ * an application and expected to be aligned with the application window.
+ * 3) {@link CompatibilityInfo#X_SHIFT_WINDOW}
+ * Create the window with adjusted x- coordinates. This is typically used
+ * in popup window, where it has to be placed relative to main window.
+ * 4) {@link CompatibilityInfo#X_SHIFT_NONE}
+ * No adjustment required, such as dialog.
+ * 5) Same as X_SHIFT_WINDOW, but no scaling. This is used by {@link SurfaceView}, which
+ * does not require scaling, but its window's location has to be adjusted.
+ *
+ * @param params the window's parameter
+ */
+ public Translator getTranslator(WindowManager.LayoutParams params) {
+ if ( (compatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
+ == CompatibilityInfo.EXPANDABLE) {
+ if (DBG) Log.d(TAG, "no translation required");
+ return null;
+ }
+
+ if ((compatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) {
+ if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) {
+ if (DBG) Log.d(TAG, "translation for surface view selected");
+ return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f);
+ } else {
+ int shiftMode;
+ if (params.gravity == Gravity.NO_GRAVITY) {
+ // For Regular Application window
+ shiftMode = X_SHIFT_AND_CLIP_CONTENT;
+ if (DBG) Log.d(TAG, "shift and clip translator");
+ } else if (params.width == WindowManager.LayoutParams.FILL_PARENT) {
+ // For Regular Application window
+ shiftMode = X_SHIFT_CONTENT;
+ if (DBG) Log.d(TAG, "shift content translator");
+ } else if ((params.gravity & Gravity.LEFT) != 0 && params.x > 0) {
+ shiftMode = X_SHIFT_WINDOW;
+ if (DBG) Log.d(TAG, "shift window translator");
+ } else {
+ shiftMode = X_SHIFT_NONE;
+ if (DBG) Log.d(TAG, "no content/window translator");
+ }
+ return new Translator(shiftMode);
+ }
+ } else if (isScalingRequired()) {
+ return new Translator();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * A helper object to translate the screen and window coordinates back and forth.
+ * @hide
+ */
+ public class Translator {
+ final private int mShiftMode;
+ final public boolean scalingRequired;
+ final public float applicationScale;
+ final public float applicationInvertedScale;
+
+ private Rect mContentInsetsBuffer = null;
+ private Rect mVisibleInsets = null;
+
+ Translator(int shiftMode, boolean scalingRequired, float applicationScale,
+ float applicationInvertedScale) {
+ mShiftMode = shiftMode;
+ this.scalingRequired = scalingRequired;
+ this.applicationScale = applicationScale;
+ this.applicationInvertedScale = applicationInvertedScale;
+ }
+
+ Translator(int shiftMode) {
+ this(shiftMode,
+ isScalingRequired(),
+ CompatibilityInfo.this.applicationScale,
+ CompatibilityInfo.this.applicationInvertedScale);
+ }
+
+ Translator() {
+ this(X_SHIFT_NONE);
+ }
+
+ /**
+ * Translate the screen rect to the application frame.
+ */
+ public void translateRectInScreenToAppWinFrame(Rect rect) {
+ if (rect.isEmpty()) return; // skip if the window size is empty.
+ switch (mShiftMode) {
+ case X_SHIFT_AND_CLIP_CONTENT:
+ rect.intersect(0, 0, mWidth, mHeight);
+ break;
+ case X_SHIFT_CONTENT:
+ rect.intersect(0, 0, mWidth + mXOffset, mHeight);
+ break;
+ case X_SHIFT_WINDOW:
+ case X_SHIFT_NONE:
+ break;
+ }
+ if (scalingRequired) {
+ rect.scale(applicationInvertedScale);
+ }
+ }
+
+ /**
+ * Translate the region in window to screen.
+ */
+ public void translateRegionInWindowToScreen(Region transparentRegion) {
+ switch (mShiftMode) {
+ case X_SHIFT_AND_CLIP_CONTENT:
+ case X_SHIFT_CONTENT:
+ transparentRegion.scale(applicationScale);
+ transparentRegion.translate(mXOffset, 0);
+ break;
+ case X_SHIFT_WINDOW:
+ case X_SHIFT_NONE:
+ transparentRegion.scale(applicationScale);
+ }
+ }
+
+ /**
+ * Apply translation to the canvas that is necessary to draw the content.
+ */
+ public void translateCanvas(Canvas canvas) {
+ if (mShiftMode == X_SHIFT_CONTENT ||
+ mShiftMode == X_SHIFT_AND_CLIP_CONTENT) {
+ // TODO: clear outside when rotation is changed.
+
+ // Translate x-offset only when the content is shifted.
+ canvas.translate(mXOffset, 0);
+ }
+ if (scalingRequired) {
+ canvas.scale(applicationScale, applicationScale);
+ }
+ }
+
+ /**
+ * Translate the motion event captured on screen to the application's window.
+ */
+ public void translateEventInScreenToAppWindow(MotionEvent event) {
+ if (mShiftMode == X_SHIFT_CONTENT ||
+ mShiftMode == X_SHIFT_AND_CLIP_CONTENT) {
+ event.translate(-mXOffset, 0);
+ }
+ if (scalingRequired) {
+ event.scale(applicationInvertedScale);
+ }
+ }
+
+ /**
+ * Translate the window's layout parameter, from application's view to
+ * Screen's view.
+ */
+ public void translateWindowLayout(WindowManager.LayoutParams params) {
+ switch (mShiftMode) {
+ case X_SHIFT_NONE:
+ case X_SHIFT_AND_CLIP_CONTENT:
+ case X_SHIFT_CONTENT:
+ params.scale(applicationScale);
+ break;
+ case X_SHIFT_WINDOW:
+ params.scale(applicationScale);
+ params.x += mXOffset;
+ break;
+ }
+ }
+
+ /**
+ * Translate a Rect in application's window to screen.
+ */
+ public void translateRectInAppWindowToScreen(Rect rect) {
+ // TODO Auto-generated method stub
+ if (scalingRequired) {
+ rect.scale(applicationScale);
+ }
+ switch(mShiftMode) {
+ case X_SHIFT_NONE:
+ case X_SHIFT_WINDOW:
+ break;
+ case X_SHIFT_CONTENT:
+ case X_SHIFT_AND_CLIP_CONTENT:
+ rect.offset(mXOffset, 0);
+ break;
+ }
+ }
+
+ /**
+ * Translate a Rect in screen coordinates into the app window's coordinates.
+ */
+ public void translateRectInScreenToAppWindow(Rect rect) {
+ switch (mShiftMode) {
+ case X_SHIFT_NONE:
+ case X_SHIFT_WINDOW:
+ break;
+ case X_SHIFT_CONTENT: {
+ rect.intersects(mXOffset, 0, rect.right, rect.bottom);
+ int dx = Math.min(mXOffset, rect.left);
+ rect.offset(-dx, 0);
+ break;
+ }
+ case X_SHIFT_AND_CLIP_CONTENT: {
+ rect.intersects(mXOffset, 0, mWidth + mXOffset, mHeight);
+ int dx = Math.min(mXOffset, rect.left);
+ rect.offset(-dx, 0);
+ break;
+ }
+ }
+ if (scalingRequired) {
+ rect.scale(applicationInvertedScale);
+ }
+ }
+
+ /**
+ * Translate the location of the sub window.
+ * @param params
+ */
+ public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
+ if (scalingRequired) {
+ params.scale(applicationScale);
+ }
+ switch (mShiftMode) {
+ // the window location on these mode does not require adjustmenet.
+ case X_SHIFT_NONE:
+ case X_SHIFT_WINDOW:
+ break;
+ case X_SHIFT_CONTENT:
+ case X_SHIFT_AND_CLIP_CONTENT:
+ params.x += mXOffset;
+ break;
+ }
+ }
+
+ /**
+ * Translate the content insets in application window to Screen. This uses
+ * the internal buffer for content insets to avoid extra object allocation.
+ */
+ public Rect getTranslatedContentInsets(Rect contentInsets) {
+ if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
+ mContentInsetsBuffer.set(contentInsets);
+ translateRectInAppWindowToScreen(mContentInsetsBuffer);
+ return mContentInsetsBuffer;
+ }
+
+ /**
+ * Translate the visible insets in application window to Screen. This uses
+ * the internal buffer for content insets to avoid extra object allocation.
+ */
+ public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
+ if (mVisibleInsets == null) mVisibleInsets = new Rect();
+ mVisibleInsets.set(visibleInsets);
+ translateRectInAppWindowToScreen(mVisibleInsets);
+ return mVisibleInsets;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 71dbd38..cb9d46e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -158,10 +158,10 @@
}
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
- if (!mCompatibilityInfo.mScalingRequired) {
- mPreloadedDrawables = sPreloadedDrawables;
- } else {
+ if (mCompatibilityInfo.isScalingRequired()) {
mPreloadedDrawables = emptySparseArray();
+ } else {
+ mPreloadedDrawables = sPreloadedDrawables;
}
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 1064fb6..d48cbd5 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -377,6 +377,8 @@
if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
mLastCallingPid = callingPid;
return setEnableApn(Phone.APN_TYPE_MMS, true);
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DEFAULT)) {
+ return setEnableApn(Phone.APN_TYPE_DEFAULT_FEATURE, true);
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
return setEnableApn(Phone.APN_TYPE_SUPL, true);
} else {
@@ -399,6 +401,8 @@
public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
return setEnableApn(Phone.APN_TYPE_MMS, false);
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DEFAULT)) {
+ return setEnableApn(Phone.APN_TYPE_DEFAULT_FEATURE, false);
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
return setEnableApn(Phone.APN_TYPE_SUPL, false);
} else {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 333c7cb..1214abc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -573,7 +573,21 @@
* directly to a gid.
*/
public static final native int getGidForName(String name);
-
+
+ /**
+ * Returns a uid for a currently running process.
+ * @param pid the process id
+ * @return the uid of the process, or -1 if the process is not running.
+ * @hide pending API council review
+ */
+ public static final int getUidForPid(int pid) {
+ String[] procStatusLabels = { "Uid:" };
+ long[] procStatusValues = new long[1];
+ procStatusValues[0] = -1;
+ Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+ return (int) procStatusValues[0];
+ }
+
/**
* Set the priority of a thread, based on Linux priorities.
*
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 48dc3ae..5cc3315 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -16,6 +16,9 @@
package android.provider;
+import java.util.Arrays;
+import java.util.List;
+
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
@@ -127,6 +130,19 @@
public static final String STARRED = "starred";
/**
+ * A custom ringtone associated with a person. Not always present.
+ * <P>Type: TEXT (URI to the ringtone)</P>
+ */
+ public static final String CUSTOM_RINGTONE = "custom_ringtone";
+
+ /**
+ * Whether the person should always be sent to voicemail. Not always
+ * present.
+ * <P>Type: INTEGER (0 for false, 1 for true)</P>
+ */
+ public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
+
+ /**
* Reference to the row in the data table holding the primary phone number.
* <P>Type: INTEGER REFERENCES data(_id)</P>
*/
@@ -143,12 +159,6 @@
* <P>Type: INTEGER REFERENCES data(_id)</P>
*/
public static final String PHOTO_ID = "photo_id";
-
- /**
- * Reference to a row containing custom ringtone and send to voicemail information.
- * <P>Type: INTEGER REFERENCES data(_id)</P>
- */
- public static final String CUSTOM_RINGTONE_ID = "custom_ringtone_id";
}
/**
@@ -261,6 +271,19 @@
private Contacts() {}
/**
+ * The package name that owns this contact and all of its details. This
+ * package has control over the {@link #IS_RESTRICTED} flag, and can
+ * grant {@link RestrictionExceptions} to other packages.
+ */
+ public static final String PACKAGE = "package";
+
+ /**
+ * Flag indicating that this data entry has been restricted by the owner
+ * {@link #PACKAGE}.
+ */
+ public static final String IS_RESTRICTED = "is_restricted";
+
+ /**
* A reference to the {@link Accounts#_ID} that this data belongs to.
*/
public static final String ACCOUNTS_ID = "accounts_id";
@@ -332,11 +355,6 @@
private interface DataColumns {
/**
- * The package name that defines this type of data.
- */
- public static final String PACKAGE = "package";
-
- /**
* The mime-type of the item represented by this row.
*/
public static final String MIMETYPE = "mimetype";
@@ -361,12 +379,6 @@
public static final String IS_SUPER_PRIMARY = "is_super_primary";
/**
- * Flag indicating that this data entry has been restricted by the owner
- * {@link #PACKAGE}.
- */
- public static final String IS_RESTRICTED = "is_restricted";
-
- /**
* The version of this data record. This is a read-only value. The data column is
* guaranteed to not change without the version going up. This value is monotonically
* increasing.
@@ -519,6 +531,18 @@
}
/**
+ * Returns the precedence of the status code the higher number being the higher precedence.
+ *
+ * @param status The status code.
+ * @return An integer representing the precedence, 0 being the lowest.
+ */
+ public static final int getPresencePrecedence(int status) {
+ // Keep this function here incase we want to enforce a different precedence than the
+ // natural order of the status constants.
+ return status;
+ }
+
+ /**
* The MIME type of {@link #CONTENT_URI} providing a directory of
* presence details.
*/
@@ -914,28 +938,6 @@
}
/**
- * Custom ringtone associated with the contact.
- */
- public static final class CustomRingtone implements BaseCommonColumns {
- private CustomRingtone() {}
-
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/custom_ringtone";
-
- /**
- * Whether to send the number to voicemail.
- * <P>Type: INTEGER (if set, non-0 means true)</P>
- */
- public static final String SEND_TO_VOICEMAIL = "data1";
-
- /**
- * The ringtone uri.
- * <P>Type: TEXT</P>
- */
- public static final String RINGTONE_URI = "data2";
- }
-
- /**
* Group Membership.
*/
public static final class GroupMembership implements BaseCommonColumns {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ebe54fcd..303d3fc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1290,6 +1290,50 @@
public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
/**
+ * CDMA only settings
+ * DTMF tone type played by the dialer when dialing.
+ * 0 = Normal
+ * 1 = Long
+ * @hide
+ */
+ public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
+
+ /**
+ * CDMA only settings
+ * Emergency Tone 0 = Off
+ * 1 = Alert
+ * 2 = Vibrate
+ * @hide
+ */
+ public static final String EMERGENCY_TONE = "emergency_tone";
+
+ /**
+ * CDMA only settings
+ * Whether the auto retry is enabled. The value is
+ * boolean (1 or 0).
+ * @hide
+ */
+ public static final String CALL_AUTO_RETRY = "call_auto_retry";
+
+ /**
+ * Whether the hearing aid is enabled. The value is
+ * boolean (1 or 0).
+ * @hide
+ */
+ public static final String HEARING_AID = "hearing_aid";
+
+ /**
+ * CDMA only settings
+ * TTY Mode
+ * 0 = OFF
+ * 1 = FULL
+ * 2 = VCO
+ * 3 = HCO
+ * @hide
+ */
+ public static final String TTY_MODE = "tty_mode";
+
+ /**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
* boolean (1 or 0).
*/
@@ -1884,6 +1928,12 @@
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
/**
+ * Whether assisted GPS should be enabled or not.
+ * @hide
+ */
+ public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+
+ /**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
* @deprecated This identifier is poorly initialized and has
@@ -2712,6 +2762,12 @@
"gtalk_max_retries_for_auth_expired";
/**
+ * This is the url for getting the app token for server-to-device data messaging.
+ */
+ public static final String DATA_MESSAGE_GET_APP_TOKEN_URL =
+ "data_messaging_get_app_token_url";
+
+ /**
* Enable use of ssl session caching.
* 'db' - save each session in a (per process) database
* 'file' - save each session in a (per process) file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index c064284..1502d98 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -46,10 +46,6 @@
* Denotes a generic operation failure.
*/
public static final int TTS_ERROR = -1;
- /**
- * Denotes a failure due to a missing resource.
- */
- public static final int TTS_ERROR_MISSING_RESOURCE = -2;
/**
* Queue mode where all entries in the playback queue (media to be played
@@ -61,6 +57,37 @@
*/
public static final int TTS_QUEUE_ADD = 1;
+
+ /**
+ * Denotes the language is available exactly as specified by the locale
+ */
+ public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2;
+
+
+ /**
+ * Denotes the language is available for the language and country specified
+ * by the locale, but not the variant.
+ */
+ public static final int TTS_LANG_COUNTRY_AVAILABLE = 1;
+
+
+ /**
+ * Denotes the language is available for the language by the locale,
+ * but not the country and variant.
+ */
+ public static final int TTS_LANG_AVAILABLE = 0;
+
+ /**
+ * Denotes the language data is missing.
+ */
+ public static final int TTS_LANG_MISSING_DATA = -1;
+
+ /**
+ * Denotes the language is not supported by the current TTS engine.
+ */
+ public static final int TTS_LANG_NOT_SUPPORTED = -2;
+
+
/**
* Called when the TTS has initialized.
*
@@ -72,15 +99,6 @@
}
/**
- * Called when the TTS has finished speaking by itself (speaking
- * finished without being canceled).
- *
- */
- public interface OnSpeechCompletedListener {
- public void onSpeechCompleted();
- }
-
- /**
* Internal constants for the TTS functionality
*
* {@hide}
@@ -100,6 +118,16 @@
public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
+
+ // keys for the parameters passed with speak commands
+ public static final String TTS_KEY_PARAM_RATE = "rate";
+ public static final String TTS_KEY_PARAM_LANGUAGE = "language";
+ public static final String TTS_KEY_PARAM_COUNTRY = "country";
+ public static final String TTS_KEY_PARAM_VARIANT = "variant";
+ public static final int TTS_PARAM_POSITION_RATE = 0;
+ public static final int TTS_PARAM_POSITION_LANGUAGE = 2;
+ public static final int TTS_PARAM_POSITION_COUNTRY = 4;
+ public static final int TTS_PARAM_POSITION_VARIANT = 6;
}
/**
@@ -112,11 +140,11 @@
private OnInitListener mInitListener = null;
private boolean mStarted = false;
private final Object mStartLock = new Object();
- private ITtsCallback mITtsCallback;
- private OnSpeechCompletedListener mSpeechCompListener = null;
- private final Object mSpeechCompListenerLock = new Object();
-
-
+ private int mCachedRate = Engine.FALLBACK_TTS_DEFAULT_RATE;
+ private String mCachedLang = Engine.FALLBACK_TTS_DEFAULT_LANG;
+ private String mCachedCountry = Engine.FALLBACK_TTS_DEFAULT_COUNTRY;
+ private String mCachedVariant = Engine.FALLBACK_TTS_DEFAULT_VARIANT;
+ private String[] mCachedParams;
/**
* The constructor for the TTS.
@@ -130,24 +158,23 @@
public TextToSpeech(Context context, OnInitListener listener) {
mContext = context;
mInitListener = listener;
+
+ mCachedParams = new String[2*4]; //4 parameters, store key and value
+ mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE;
+ mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE;
+ mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY;
+ mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT;
+ updateCachedParamArray();
+
initTts();
}
- public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) {
- synchronized(mSpeechCompListenerLock) {
- mSpeechCompListener = listener;
- }
- }
-
-
- private boolean dataFilesCheck() {
- // TODO #TTS# config manager will be in settings
- Log.i("TTS_FIXME", "FIXME in Tts: config manager will be in settings");
- // TODO #TTS# implement checking of the correct installation of
- // the data files.
-
- return true;
+ private void updateCachedParamArray() {
+ mCachedParams[Engine.TTS_PARAM_POSITION_RATE+1] = String.valueOf(mCachedRate);
+ mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE+1] = mCachedLang;
+ mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY+1] = mCachedCountry;
+ mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT+1] = mCachedVariant;
}
@@ -159,34 +186,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized(mStartLock) {
mITts = ITts.Stub.asInterface(service);
- try {
- mITtsCallback = new ITtsCallback.Stub() {
- public void markReached(String mark)
- throws RemoteException {
- // call the listener of that event, but not
- // while locked.
- OnSpeechCompletedListener listener = null;
- synchronized(mSpeechCompListenerLock) {
- listener = mSpeechCompListener;
- }
- if (listener != null) {
- listener.onSpeechCompleted();
- }
- }
- };
- mITts.registerCallback(mITtsCallback);
-
- } catch (RemoteException e) {
- initTts();
- return;
- }
-
mStarted = true;
- // The callback can become null if the Android OS decides to
- // restart the TTS process as well as whatever is using it.
- // In such cases, do nothing - the error handling from the
- // speaking calls will kick in and force a proper restart of
- // the TTS.
if (mInitListener != null) {
// TODO manage failures and missing resources
mInitListener.onInit(TTS_SUCCESS);
@@ -333,8 +333,8 @@
return;
}
try {
- // TODO support extra parameters, passing null for the moment
- mITts.speak(text, queueMode, null);
+ // TODO support extra parameters, passing cache of current parameters for the moment
+ mITts.speak(text, queueMode, mCachedParams);
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -353,6 +353,27 @@
/**
+ * Speaks the IPA string using the specified queuing strategy and speech
+ * parameters. Note that the speech parameters are not universally supported
+ * by all engines and will be treated as a hint. The TTS library will try to
+ * fulfill these parameters as much as possible, but there is no guarantee
+ * that the voice used will have the properties specified.
+ *
+ * @param ipaText
+ * The string of IPA text to be spoken.
+ * @param queueMode
+ * The queuing strategy to use.
+ * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
+ * @param params
+ * The hashmap of speech parameters to be used.
+ */
+ public void speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
+ {
+ //TODO: Implement speakIpa
+ }
+
+
+ /**
* Plays the earcon using the specified queueing mode and parameters.
*
* @param earcon
@@ -470,7 +491,9 @@
}
try {
if (speechRate > 0) {
- mITts.setSpeechRate((int)(speechRate*100));
+ mCachedRate = (int)(speechRate*100);
+ updateCachedParamArray();
+ mITts.setSpeechRate(mCachedRate);
}
} catch (RemoteException e) {
// TTS died; restart it.
@@ -528,7 +551,11 @@
return;
}
try {
- mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant());
+ mCachedLang = loc.getISO3Language();
+ mCachedCountry = loc.getISO3Country();
+ mCachedVariant = loc.getVariant();
+ updateCachedParamArray();
+ mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -537,6 +564,20 @@
}
}
+ /**
+ * Checks if the specified language as represented by the locale is available.
+ *
+ * @param loc
+ * The locale describing the language to be used.
+ * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+ TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
+ */
+ public int isLanguageAvailable(Locale loc) {
+ //TODO: Implement isLanguageAvailable
+ return TTS_LANG_NOT_SUPPORTED;
+ }
+
+
/**
* Speaks the given text using the specified queueing mode and parameters.
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index a095913..d89ada0 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -106,16 +106,8 @@
* {@hide}
*/
public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) {
- if (compatibilityInfo.mScalingRequired) {
- float invertedRatio = compatibilityInfo.mApplicationInvertedScale;
- density *= invertedRatio;
- scaledDensity *= invertedRatio;
- xdpi *= invertedRatio;
- ydpi *= invertedRatio;
- widthPixels *= invertedRatio;
- heightPixels *= invertedRatio;
- }
- if (!compatibilityInfo.mConfiguredExpandable) {
+ int xOffset = 0;
+ if (!compatibilityInfo.isConfiguredExpandable()) {
// Note: this assume that configuration is updated before calling
// updateMetrics method.
int defaultWidth;
@@ -141,11 +133,13 @@
if (defaultWidth == widthPixels && defaultHeight == heightPixels) {
// the screen size is same as expected size. make it expandable
- compatibilityInfo.mExpandable = true;
+ compatibilityInfo.setExpandable(true);
} else {
- compatibilityInfo.mExpandable = false;
+ compatibilityInfo.setExpandable(false);
// adjust the size only when the device's screen is bigger.
if (defaultWidth < widthPixels) {
+ // content/window's x offset in original pixels
+ xOffset = ((widthPixels - defaultWidth) / 2);
widthPixels = defaultWidth;
}
if (defaultHeight < heightPixels) {
@@ -153,8 +147,19 @@
}
}
}
+ compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels);
+ if (compatibilityInfo.isScalingRequired()) {
+ float invertedRatio = compatibilityInfo.applicationInvertedScale;
+ density *= invertedRatio;
+ scaledDensity *= invertedRatio;
+ xdpi *= invertedRatio;
+ ydpi *= invertedRatio;
+ widthPixels *= invertedRatio;
+ heightPixels *= invertedRatio;
+ }
}
+ @Override
public String toString() {
return "DisplayMetrics{density=" + density + ", width=" + widthPixels +
", height=" + heightPixels + ", scaledDensity=" + scaledDensity +
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 69c6a7c..0e37b26 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -270,28 +270,47 @@
}
/**
- * Scales down the cood of this event by the given scale.
+ * Scales down the coordination of this event by the given scale.
*
* @hide
*/
public void scale(float scale) {
- if (scale != 1.0f) {
- mX *= scale;
- mY *= scale;
- mRawX *= scale;
- mRawY *= scale;
- mSize *= scale;
- mXPrecision *= scale;
- mYPrecision *= scale;
- if (mHistory != null) {
- float[] history = mHistory;
- int length = history.length;
- for (int i = 0; i < length; i += 4) {
- history[i] *= scale; // X
- // history[i + 2] == pressure
- history[i + 1] *= scale; // Y
- history[i + 3] *= scale; // Size, TODO: square this?
- }
+ mX *= scale;
+ mY *= scale;
+ mRawX *= scale;
+ mRawY *= scale;
+ mSize *= scale;
+ mXPrecision *= scale;
+ mYPrecision *= scale;
+ if (mHistory != null) {
+ float[] history = mHistory;
+ int length = history.length;
+ for (int i = 0; i < length; i += 4) {
+ history[i] *= scale; // X
+ history[i + 1] *= scale; // Y
+ // no need to scale pressure ([i+2])
+ history[i + 3] *= scale; // Size, TODO: square this?
+ }
+ }
+ }
+
+ /**
+ * Translate the coordination of the event by given x and y.
+ *
+ * @hide
+ */
+ public void translate(float dx, float dy) {
+ mX += dx;
+ mY += dy;
+ mRawX += dx;
+ mRawY += dx;
+ if (mHistory != null) {
+ float[] history = mHistory;
+ int length = history.length;
+ for (int i = 0; i < length; i += 4) {
+ history[i] += dx; // X
+ history[i + 1] += dy; // Y
+ // no need to translate pressure (i+2) and size (i+3)
}
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6519852..1e2bc1f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.CompatibilityInfo;
+import android.content.res.CompatibilityInfo.Translator;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -139,24 +140,21 @@
int mFormat = -1;
int mType = -1;
final Rect mSurfaceFrame = new Rect();
- private final CompatibilityInfo mCompatibilityInfo;
+ private Translator mTranslator;
public SurfaceView(Context context) {
super(context);
setWillNotDraw(true);
- mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
public SurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(true);
- mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setWillNotDraw(true);
- mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
/**
@@ -259,9 +257,9 @@
public boolean dispatchTouchEvent(MotionEvent event) {
// SurfaceView uses pre-scaled size unless fixed size is requested. This hook
// scales the event back to the pre-scaled coordinates for such surface.
- if (mRequestedWidth < 0 && mCompatibilityInfo.mScalingRequired) {
+ if (mRequestedWidth < 0 && mTranslator != null) {
MotionEvent scaledBack = MotionEvent.obtain(event);
- scaledBack.scale(mCompatibilityInfo.mApplicationScale);
+ scaledBack.scale(mTranslator.applicationScale);
try {
return super.dispatchTouchEvent(scaledBack);
} finally {
@@ -298,15 +296,18 @@
if (!mHaveFrame) {
return;
}
- float appScale = mCompatibilityInfo.mApplicationScale;
+ mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator;
+
+ float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
int myWidth = mRequestedWidth;
if (myWidth <= 0) myWidth = getWidth();
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
- // Use original size for surface unless fixed size is requested.
- if (mRequestedWidth <= 0 && mCompatibilityInfo.mScalingRequired) {
+ // Use original size if the app specified the size of the view,
+ // and let the flinger to scale up.
+ if (mRequestedWidth <= 0 && mTranslator != null && mTranslator.scalingRequired) {
myWidth *= appScale;
myHeight *= appScale;
}
@@ -326,7 +327,7 @@
+ " visible=" + visibleChanged
+ " left=" + (mLeft != mLocation[0])
+ " top=" + (mTop != mLocation[1]));
-
+
try {
final boolean visible = mVisible = mRequestedVisible;
mLeft = mLocation[0];
@@ -336,16 +337,23 @@
mFormat = mRequestedFormat;
mType = mRequestedType;
- // Scaling window's layout here because mLayout is not used elsewhere.
- mLayout.x = (int) (mLeft * appScale);
- mLayout.y = (int) (mTop * appScale);
- mLayout.width = (int) (getWidth() * appScale);
- mLayout.height = (int) (getHeight() * appScale);
+ // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+
+ // Places the window relative
+ mLayout.x = mLeft;
+ mLayout.y = mTop;
+ mLayout.width = getWidth();
+ mLayout.height = getHeight();
+ if (mTranslator != null) {
+ mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
+ }
+
mLayout.format = mRequestedFormat;
mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_SCALED
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING
;
mLayout.memoryType = mRequestedType;
@@ -372,13 +380,6 @@
visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
mVisibleInsets, mSurface);
- if (mCompatibilityInfo.mScalingRequired) {
- float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
- mContentInsets.scale(invertedScale);
- mVisibleInsets.scale(invertedScale);
- mWinFrame.scale(invertedScale);
- }
-
if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
@@ -447,24 +448,14 @@
private static class MyWindow extends IWindow.Stub {
private final WeakReference<SurfaceView> mSurfaceView;
- private final CompatibilityInfo mCompatibilityInfo;
public MyWindow(SurfaceView surfaceView) {
mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
- mCompatibilityInfo = surfaceView.getContext().getResources().getCompatibilityInfo();
}
public void resized(int w, int h, Rect coveredInsets,
Rect visibleInsets, boolean reportDraw) {
SurfaceView surfaceView = mSurfaceView.get();
- if (mCompatibilityInfo.mScalingRequired) {
- float scale = mCompatibilityInfo.mApplicationInvertedScale;
- w *= scale;
- h *= scale;
- coveredInsets.scale(scale);
- visibleInsets.scale(scale);
- }
-
if (surfaceView != null) {
if (localLOGV) Log.v(
"SurfaceView", surfaceView + " got resized: w=" +
@@ -627,9 +618,6 @@
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
Rect frame = dirty != null ? dirty : mSurfaceFrame;
- if (mCompatibilityInfo.mScalingRequired) {
- frame.scale(mCompatibilityInfo.mApplicationScale);
- }
try {
c = mSurface.lockCanvas(frame);
} catch (Exception e) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f17f0e4..3bfdde8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6543,7 +6543,7 @@
boolean changed = false;
if (DBG) {
- System.out.println(this + " View.setFrame(" + left + "," + top + ","
+ Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index fe5edc2..a12b14a 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -131,13 +131,12 @@
Rect mDirty; // will be a graphics.Region soon
boolean mIsAnimating;
- private CompatibilityInfo mCompatibilityInfo;
+ CompatibilityInfo.Translator mTranslator;
final View.AttachInfo mAttachInfo;
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
- final Point mVisPoint; // used to retrieve global offset of focused view.
boolean mTraversalScheduled;
boolean mWillDrawSoon;
@@ -225,7 +224,6 @@
mDirty = new Rect();
mTempRect = new Rect();
mVisRect = new Rect();
- mVisPoint = new Point();
mWinFrame = new Rect();
mWindow = new W(this, context);
mInputMethodCallback = new InputMethodCallback(this);
@@ -394,20 +392,25 @@
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
- mCompatibilityInfo = mView.getContext().getResources().getCompatibilityInfo();
+
+ CompatibilityInfo compatibilityInfo =
+ mView.getContext().getResources().getCompatibilityInfo();
+ mTranslator = compatibilityInfo.getTranslator(attrs);
boolean restore = false;
- if (mCompatibilityInfo.mScalingRequired || !mCompatibilityInfo.mExpandable) {
+ if (attrs != null && mTranslator != null) {
restore = true;
- mWindowAttributes.backup();
+ attrs.backup();
+ mTranslator.translateWindowLayout(attrs);
}
- if (!mCompatibilityInfo.mExpandable) {
- adjustWindowAttributesForCompatibleMode(mWindowAttributes);
- }
+ if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
+
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
- mAttachInfo.mScalingRequired = mCompatibilityInfo.mScalingRequired;
- mAttachInfo.mApplicationScale = mCompatibilityInfo.mApplicationScale;
+ mAttachInfo.mScalingRequired =
+ mTranslator == null ? false : mTranslator.scalingRequired;
+ mAttachInfo.mApplicationScale =
+ mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
@@ -428,15 +431,14 @@
mAttachInfo.mRootView = null;
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
+ } finally {
+ if (restore) {
+ attrs.restore();
+ }
}
- if (restore) {
- mWindowAttributes.restore();
- }
-
- if (mCompatibilityInfo.mScalingRequired) {
- mAttachInfo.mContentInsets.scale(
- mCompatibilityInfo.mApplicationInvertedScale);
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
@@ -548,14 +550,14 @@
public void invalidateChild(View child, Rect dirty) {
checkThread();
- if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
- if (mCurScrollY != 0 || mCompatibilityInfo.mScalingRequired) {
+ if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+ if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
if (mCurScrollY != 0) {
mTempRect.offset(0, -mCurScrollY);
}
- if (mCompatibilityInfo.mScalingRequired) {
- mTempRect.scale(mCompatibilityInfo.mApplicationScale);
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mTempRect);
}
dirty = mTempRect;
}
@@ -574,7 +576,7 @@
return null;
}
- public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+ public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
if (child != mView) {
throw new RuntimeException("child is not mine, honest!");
}
@@ -635,14 +637,14 @@
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
- float appScale = mCompatibilityInfo.mApplicationScale;
+ float appScale = mAttachInfo.mApplicationScale;
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
params = lp;
}
-
+ Rect frame = mWinFrame;
if (mFirst) {
fullRedrawNeeded = true;
mLayoutRequested = true;
@@ -667,11 +669,11 @@
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
- desiredWindowWidth = mWinFrame.width();
- desiredWindowHeight = mWinFrame.height();
+ desiredWindowWidth = frame.width();
+ desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v("ViewRoot",
- "View " + host + " resized to: " + mWinFrame);
+ "View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowResizesToFitContent = true;
@@ -817,7 +819,6 @@
}
}
- final Rect frame = mWinFrame;
boolean initialized = false;
boolean contentInsetsChanged = false;
boolean visibleInsetsChanged;
@@ -890,7 +891,7 @@
} catch (RemoteException e) {
}
if (DEBUG_ORIENTATION) Log.v(
- "ViewRoot", "Relayout returned: frame=" + mWinFrame + ", surface=" + mSurface);
+ "ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface);
attachInfo.mWindowLeft = frame.left;
attachInfo.mWindowTop = frame.top;
@@ -965,7 +966,6 @@
if (Config.DEBUG && ViewDebug.profileLayout) {
startTime = SystemClock.elapsedRealtime();
}
-
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -992,7 +992,10 @@
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
- mTransparentRegion.scale(appScale);
+ if (mTranslator != null) {
+ mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
+ }
+
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
// reconfigure window manager
@@ -1023,15 +1026,17 @@
= givenContent.bottom = givenVisible.left = givenVisible.top
= givenVisible.right = givenVisible.bottom = 0;
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
- if (mCompatibilityInfo.mScalingRequired) {
- insets.contentInsets.scale(appScale);
- insets.visibleInsets.scale(appScale);
+ Rect contentInsets = insets.contentInsets;
+ Rect visibleInsets = insets.visibleInsets;
+ if (mTranslator != null) {
+ contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
+ visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
}
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
- insets.contentInsets, insets.visibleInsets);
+ contentInsets, visibleInsets);
} catch (RemoteException e) {
}
}
@@ -1174,8 +1179,8 @@
mCurScrollY = yoff;
fullRedrawNeeded = true;
}
- float appScale = mCompatibilityInfo.mApplicationScale;
- boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
+ float appScale = mAttachInfo.mApplicationScale;
+ boolean scalingRequired = mAttachInfo.mScalingRequired;
Rect dirty = mDirty;
if (mUseGL) {
@@ -1194,8 +1199,8 @@
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
- if (scalingRequired) {
- canvas.scale(appScale, appScale);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
}
mView.draw(canvas);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -1246,7 +1251,6 @@
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
-
canvas = surface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right ||
@@ -1302,8 +1306,8 @@
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
- if (scalingRequired) {
- canvas.scale(appScale, appScale);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
}
mView.draw(canvas);
} finally {
@@ -1616,10 +1620,9 @@
} else {
didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
}
- if (event != null && mCompatibilityInfo.mScalingRequired) {
- event.scale(mCompatibilityInfo.mApplicationInvertedScale);
+ if (event != null && mTranslator != null) {
+ mTranslator.translateEventInScreenToAppWindow(event);
}
-
try {
boolean handled;
if (mView != null && mAdded && event != null) {
@@ -1711,6 +1714,7 @@
case RESIZED:
Rect coveredInsets = ((Rect[])msg.obj)[0];
Rect visibleInsets = ((Rect[])msg.obj)[1];
+
if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
&& mPendingContentInsets.equals(coveredInsets)
&& mPendingVisibleInsets.equals(visibleInsets)) {
@@ -1745,7 +1749,7 @@
if (mGlWanted && !mUseGL) {
initializeGL();
if (mGlCanvas != null) {
- float appScale = mCompatibilityInfo.mApplicationScale;
+ float appScale = mAttachInfo.mApplicationScale;
mGlCanvas.setViewport(
(int) (mWidth * appScale), (int) (mHeight * appScale));
}
@@ -2379,18 +2383,16 @@
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
+
+ float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
- float appScale = mCompatibilityInfo.mApplicationScale;
- boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
- if (params != null && !mCompatibilityInfo.mExpandable) {
+ if (params != null && mTranslator != null) {
restore = true;
params.backup();
- adjustWindowAttributesForCompatibleMode(params);
+ mTranslator.translateWindowLayout(params);
}
- if (params != null && scalingRequired) {
- if (!restore) params.backup();
- restore = true;
- params.scale(appScale);
+ if (params != null) {
+ if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
}
int relayoutResult = sWindowSession.relayout(
mWindow, params,
@@ -2401,44 +2403,16 @@
if (restore) {
params.restore();
}
- if (scalingRequired) {
- float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
- mPendingContentInsets.scale(invertedScale);
- mPendingVisibleInsets.scale(invertedScale);
- mWinFrame.scale(invertedScale);
+
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+ mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
+ mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
}
return relayoutResult;
}
/**
- * Adjust the window's layout parameter for compatibility mode. It replaces FILL_PARENT
- * with the default window size, and centers if the window wanted to fill
- * horizontally.
- *
- * @param attrs the window's layout params to adjust
- */
- private void adjustWindowAttributesForCompatibleMode(WindowManager.LayoutParams attrs) {
- // fix app windows only
- if (attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
- // TODO: improve gravity logic
- if (attrs.width == ViewGroup.LayoutParams.FILL_PARENT) {
- attrs.width = metrics.widthPixels;
- attrs.gravity |= Gravity.CENTER_HORIZONTAL;
- mWindowAttributesChanged = attrs == mWindowAttributes;
- }
- if (attrs.height == ViewGroup.LayoutParams.FILL_PARENT) {
- attrs.height = metrics.heightPixels;
- attrs.gravity |= Gravity.TOP;
- mWindowAttributesChanged = attrs == mWindowAttributes;
- }
- if (DEBUG_LAYOUT) {
- Log.d(TAG, "Adjusted Attributes for compatibility : " + attrs);
- }
- }
- }
-
- /**
* {@inheritDoc}
*/
public void playSoundEffect(int effectId) {
@@ -2541,16 +2515,14 @@
+ " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw);
Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
- if (mCompatibilityInfo.mScalingRequired) {
- float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
- coveredInsets.scale(invertedScale);
- visibleInsets.scale(invertedScale);
- msg.arg1 = (int) (w * invertedScale);
- msg.arg2 = (int) (h * invertedScale);
- } else {
- msg.arg1 = w;
- msg.arg2 = h;
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(coveredInsets);
+ mTranslator.translateRectInScreenToAppWindow(visibleInsets);
+ w *= mTranslator.applicationInvertedScale;
+ h *= mTranslator.applicationInvertedScale;
}
+ msg.arg1 = w;
+ msg.arg2 = h;
msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
sendMessage(msg);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e295d15..bdb86d7 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -483,6 +483,12 @@
* {@hide} */
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
+ /** Window flag: special flag to let a window ignore the compatibility scaling.
+ * This is used by SurfaceView to create a window that does not scale the content.
+ *
+ * {@hide} */
+ public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000;
+
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
* it is created.
@@ -978,8 +984,9 @@
/**
* Scale the layout params' coordinates and size.
+ * @hide
*/
- void scale(float scale) {
+ public void scale(float scale) {
x *= scale;
y *= scale;
if (width > 0) {
@@ -997,14 +1004,13 @@
void backup() {
int[] backup = mCompatibilityParamsBackup;
if (backup == null) {
- // we backup 5 elements, x, y, width, height and gravity.
- backup = mCompatibilityParamsBackup = new int[5];
+ // we backup 4 elements, x, y, width, height
+ backup = mCompatibilityParamsBackup = new int[4];
}
backup[0] = x;
backup[1] = y;
backup[2] = width;
backup[3] = height;
- backup[4] = gravity;
}
/**
@@ -1018,7 +1024,6 @@
y = backup[1];
width = backup[2];
height = backup[3];
- gravity = backup[4];
}
}
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 4d471f7f..fb9edb9 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -79,6 +79,7 @@
int httpStatusCode;
long contentLength;
long expires;
+ String expiresString;
String localPath;
String lastModified;
String etag;
@@ -107,6 +108,13 @@
return expires;
}
+ /**
+ * @hide Pending API council approval
+ */
+ public String getExpiresString() {
+ return expiresString;
+ }
+
public String getLastModified() {
return lastModified;
}
@@ -603,17 +611,18 @@
if (location != null) ret.location = location;
ret.expires = -1;
- String expires = headers.getExpires();
- if (expires != null) {
+ ret.expiresString = headers.getExpires();
+ if (ret.expiresString != null) {
try {
- ret.expires = HttpDateTime.parse(expires);
+ ret.expires = HttpDateTime.parse(ret.expiresString);
} catch (IllegalArgumentException ex) {
// Take care of the special "-1" and "0" cases
- if ("-1".equals(expires) || "0".equals(expires)) {
+ if ("-1".equals(ret.expiresString)
+ || "0".equals(ret.expiresString)) {
// make it expired, but can be used for history navigation
ret.expires = 0;
} else {
- Log.e(LOGTAG, "illegal expires: " + expires);
+ Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
}
}
}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index c407044..9a8c3c0 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -147,6 +147,16 @@
}
/**
+ * Get the WebChromeClient.
+ * @return the current WebChromeClient instance.
+ *
+ *@hide pending API council approval.
+ */
+ public WebChromeClient getWebChromeClient() {
+ return mWebChromeClient;
+ }
+
+ /**
* Set the client DownloadListener.
* @param client An implementation of DownloadListener.
*/
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 89cb606..8e25395 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -42,6 +42,7 @@
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
public static final boolean WEB_SYNC_MANAGER = false;
+ public static final boolean WEB_TEXT_VIEW = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
new file mode 100644
index 0000000..973cb010
--- /dev/null
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -0,0 +1,128 @@
+/*
+ * 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.webkit;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+import java.util.HashMap;
+
+/**
+ * <p>A View that displays Videos. Instances of this class
+ * are created on the WebCore thread. However, their code
+ * executes on the UI thread. Right now there is only one
+ * such view for fullscreen video rendering.
+ *
+ */
+class HTML5VideoViewProxy extends Handler {
+ // Logging tag.
+ private static final String LOGTAG = "HTML5VideoViewProxy";
+
+ // Message Ids
+ private static final int INIT = 100;
+ private static final int PLAY = 101;
+
+ // The singleton instance.
+ private static HTML5VideoViewProxy sInstance;
+ // The VideoView driven via this proxy.
+ private VideoView mVideoView;
+ // The context object used to initialize the VideoView and the
+ // MediaController.
+ private Context mContext;
+
+ /**
+ * Private constructor.
+ * @param context is the application context.
+ */
+ private HTML5VideoViewProxy(Context context) {
+ // This handler is for the main (UI) thread.
+ super(Looper.getMainLooper());
+ // Save the context object.
+ mContext = context;
+ // Send a message to the UI thread to create the VideoView.
+ // This need to be done on the UI thread, or else the
+ // event Handlers used by the VideoView and MediaController
+ // will be attached to the wrong thread.
+ Message message = obtainMessage(INIT);
+ sendMessage(message);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ // This executes on the UI thread.
+ switch (msg.what) {
+ case INIT:
+ // Create the video view and set a default controller.
+ mVideoView = new VideoView(mContext);
+ mVideoView.setMediaController(new MediaController(mContext));
+ break;
+ case PLAY:
+ // Check if the fullscreen video view is currently playing.
+ // If it is, ignore the message.
+ if (!mVideoView.isPlaying()) {
+ HashMap<String, Object> map =
+ (HashMap<String, Object>) msg.obj;
+ String url = (String) map.get("url");
+ WebView webview = (WebView) map.get("webview");
+ WebChromeClient client = webview.getWebChromeClient();
+ if (client != null) {
+ mVideoView.setVideoURI(Uri.parse(url));
+ mVideoView.start();
+ client.onShowCustomView(mVideoView);
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ * Play a video stream.
+ * @param url is the URL of the video stream.
+ * @param webview is the WebViewCore that is requesting the playback.
+ */
+ public void play(String url, WebViewCore webviewCore) {
+ // We need to know the webview that is requesting the playback.
+ Message message = obtainMessage(PLAY);
+ HashMap<String, Object> map = new HashMap();
+ map.put("url", url);
+ map.put("webview", webviewCore.getWebView());
+ message.obj = map;
+ sendMessage(message);
+ }
+
+ /**
+ * The factory for HTML5VideoViewProxy instances. Right now,
+ * it only produces a singleton.
+ * @param webViewCore is the WebViewCore that is requesting the proxy.
+ *
+ * @return the HTML5VideoViewProxy singleton.
+ */
+ public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) {
+ if (sInstance == null) {
+ sInstance = new HTML5VideoViewProxy(webViewCore.getWebView().getContext());
+ }
+ return sInstance;
+ }
+}
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 08854f7..a87a5c2 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -949,7 +949,7 @@
final int nativeResponse = nativeCreateResponse(
mUrl, statusCode, mStatusText,
mMimeType, mContentLength, mEncoding,
- mCacheResult == null ? 0 : mCacheResult.expires / 1000);
+ mCacheResult == null ? null : mCacheResult.expiresString);
if (mHeaders != null) {
mHeaders.getHeaders(new Headers.HeaderCallback() {
public void header(String name, String value) {
@@ -1460,12 +1460,12 @@
* @param expectedLength An estimate of the content length or the length
* given by the server.
* @param encoding HTTP encoding.
- * @param expireTime HTTP expires converted to seconds since the epoch.
+ * @param expireTime HTTP expires.
* @return The native response pointer.
*/
private native int nativeCreateResponse(String url, int statusCode,
String statusText, String mimeType, long expectedLength,
- String encoding, long expireTime);
+ String encoding, String expireTime);
/**
* Add a response header to the native object.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 754b1d9..19e39ed 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.os.Message;
+import android.view.View;
public class WebChromeClient {
@@ -44,6 +45,23 @@
public void onReceivedIcon(WebView view, Bitmap icon) {}
/**
+ * Notify the host application that the current page would
+ * like to show a custom View.
+ * @param view is the View object to be shown.
+ *
+ * @hide pending council approval
+ */
+ public void onShowCustomView(View view) {}
+
+ /**
+ * Notify the host application that the current page would
+ * like to hide its custom view.
+ *
+ * @hide pending council approval
+ */
+ public void onHideCustomView() {}
+
+ /**
* Request the host application to create a new Webview. The host
* application should handle placement of the new WebView in the view
* system. The default behavior returns null.
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f57c647..0ee3a60 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -69,7 +69,24 @@
}
int value;
}
-
+
+ /**
+ * Enum for specifying the WebView's desired density.
+ * FAR makes 100% looking like in 240dpi
+ * MEDIUM makes 100% looking like in 160dpi
+ * CLOSE makes 100% looking like in 120dpi
+ * @hide Pending API council approval
+ */
+ public enum ZoomDensity {
+ FAR(150), // 240dpi
+ MEDIUM(100), // 160dpi
+ CLOSE(75); // 120dpi
+ ZoomDensity(int size) {
+ value = size;
+ }
+ int value;
+ }
+
/**
* Default cache usage pattern Use with {@link #setCacheMode}.
*/
@@ -105,6 +122,8 @@
LOW
}
+ // WebView associated with this WebSettings.
+ private WebView mWebView;
// BrowserFrame used to access the native frame pointer.
private BrowserFrame mBrowserFrame;
// Flag to prevent multiple SYNC messages at one time.
@@ -149,6 +168,7 @@
// Don't need to synchronize the get/set methods as they
// are basic types, also none of these values are used in
// native WebCore code.
+ private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM;
private RenderPriority mRenderPriority = RenderPriority.NORMAL;
private int mOverrideCacheMode = LOAD_DEFAULT;
private boolean mSaveFormData = true;
@@ -232,13 +252,13 @@
// User agent strings.
private static final String DESKTOP_USERAGENT =
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en)"
- + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
- + " Safari/525.20.1";
+ "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
+ + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
+ + " Safari/530.17";
private static final String IPHONE_USERAGENT =
- "Mozilla/5.0 (iPhone; U; CPU iPhone 2_1 like Mac OS X; en)"
- + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
- + " Mobile/5F136 Safari/525.20.1";
+ "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+ + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
+ + " Mobile/7A341 Safari/528.16";
private static Locale sLocale;
private static Object sLockForLocaleSettings;
@@ -246,9 +266,10 @@
* Package constructor to prevent clients from creating a new settings
* instance.
*/
- WebSettings(Context context) {
+ WebSettings(Context context, WebView webview) {
mEventHandler = new EventHandler();
mContext = context;
+ mWebView = webview;
mDefaultTextEncoding = context.getString(com.android.internal.
R.string.default_text_encoding);
@@ -456,6 +477,31 @@
}
/**
+ * Set the default zoom density of the page. This should be called from UI
+ * thread.
+ * @param zoom A ZoomDensity value
+ * @see WebSettings.ZoomDensity
+ * @hide Pending API council approval
+ */
+ public void setDefaultZoom(ZoomDensity zoom) {
+ if (mDefaultZoom != zoom) {
+ mDefaultZoom = zoom;
+ mWebView.updateDefaultZoomDensity(zoom.value);
+ }
+ }
+
+ /**
+ * Get the default zoom density of the page. This should be called from UI
+ * thread.
+ * @return A ZoomDensity value
+ * @see WebSettings.ZoomDensity
+ * @hide Pending API council approval
+ */
+ public ZoomDensity getDefaultZoom() {
+ return mDefaultZoom;
+ }
+
+ /**
* Enables using light touches to make a selection and activate mouseovers.
*/
public void setLightTouchEnabled(boolean enabled) {
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 4a8fa3c..d1904b7 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -24,13 +24,15 @@
import android.text.Spannable;
import android.text.TextUtils;
import android.text.method.MovementMethod;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputConnection;
import android.widget.AbsoluteLayout.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
@@ -45,6 +47,8 @@
*/
/* package */ class WebTextView extends AutoCompleteTextView {
+ static final String LOGTAG = "webtextview";
+
private WebView mWebView;
private boolean mSingle;
private int mWidthSpec;
@@ -82,6 +86,7 @@
setImeOptions(EditorInfo.IME_ACTION_NONE);
// Allow webkit's drawing to show through
setWillNotDraw(true);
+ setCursorVisible(false);
}
@Override
@@ -265,9 +270,25 @@
return ptr == mNodePointer;
}
+ @Override public InputConnection onCreateInputConnection(
+ EditorInfo outAttrs) {
+ InputConnection connection = super.onCreateInputConnection(outAttrs);
+ if (mWebView != null) {
+ // Use the name of the textfield + the url. Use backslash as an
+ // arbitrary separator.
+ outAttrs.fieldName = mWebView.nativeFocusCandidateName() + "\\"
+ + mWebView.getUrl();
+ }
+ return connection;
+ }
+
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (mWebView != null) {
+ if (DebugFlags.WEB_TEXT_VIEW) {
+ Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart
+ + " selEnd=" + selEnd);
+ }
mWebView.setSelection(selStart, selEnd);
}
}
@@ -313,6 +334,10 @@
} else {
// This corrects the selection which may have been affected by the
// trackball or auto-correct.
+ if (DebugFlags.WEB_TEXT_VIEW) {
+ Log.v(LOGTAG, "onTextChanged start=" + start
+ + " start + before=" + (start + before));
+ }
mWebView.setSelection(start, start + before);
}
if (!cannotUseKeyEvents) {
@@ -542,6 +567,10 @@
} else if (start > length) {
start = length;
}
+ if (DebugFlags.WEB_TEXT_VIEW) {
+ Log.v(LOGTAG, "setText start=" + start
+ + " end=" + end);
+ }
Selection.setSelection(span, start, end);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 186e1d1..8f50247 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -329,9 +329,18 @@
/**
* The minimum elapsed time before sending another ACTION_MOVE event to
- * WebViewCore
+ * WebViewCore. This really should be tuned for each type of the devices.
+ * For example in Google Map api test case, it takes Dream device at least
+ * 150ms to do a full cycle in the WebViewCore by processing a touch event,
+ * triggering the layout and drawing the picture. While the same process
+ * takes 60+ms on the current high speed device. If we make
+ * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
+ * to WebViewCore queue and the real layout and draw events will be pushed
+ * to further, which slows down the refresh rate. Choose 50 to favor the
+ * current high speed devices. For Dream like devices, 100 is a better
+ * choice. Maybe make this in the buildspec later.
*/
- private static final int TOUCH_SENT_INTERVAL = 100;
+ private static final int TOUCH_SENT_INTERVAL = 50;
/**
* Helper class to get velocity for fling
@@ -449,6 +458,8 @@
static final int WEBCORE_INITIALIZED_MSG_ID = 16;
static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
static final int DID_FIRST_LAYOUT_MSG_ID = 18;
+ static final int MOVE_OUT_OF_PLUGIN = 19;
+ static final int CLEAR_TEXT_ENTRY = 20;
static final int UPDATE_CLIPBOARD = 22;
static final int LONG_PRESS_CENTER = 23;
@@ -476,8 +487,8 @@
"WEBCORE_INITIALIZED_MSG_ID", // = 16;
"UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
"DID_FIRST_LAYOUT_MSG_ID", // = 18;
- "19",
- "20",
+ "MOVE_OUT_OF_PLUGIN", // = 19;
+ "CLEAR_TEXT_ENTRY", // = 20;
"21", // = 21;
"UPDATE_CLIPBOARD", // = 22;
"LONG_PRESS_CENTER", // = 23;
@@ -502,7 +513,7 @@
// default scale. Depending on the display density.
static int DEFAULT_SCALE_PERCENT;
- private float DEFAULT_SCALE;
+ private float mDefaultScale;
// set to true temporarily while the zoom control is being dragged
private boolean mPreviewZoomOnly = false;
@@ -745,7 +756,7 @@
mNavSlop = (int) (16 * density);
// density adjusted scale factors
DEFAULT_SCALE_PERCENT = (int) (100 * density);
- DEFAULT_SCALE = density;
+ mDefaultScale = density;
mActualScale = density;
mInvActualScale = 1 / density;
DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
@@ -754,6 +765,23 @@
mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
}
+ /* package */void updateDefaultZoomDensity(int zoomDensity) {
+ final float density = getContext().getResources().getDisplayMetrics().density
+ * 100 / zoomDensity;
+ if (Math.abs(density - mDefaultScale) > 0.01) {
+ float scaleFactor = density / mDefaultScale;
+ // adjust the limits
+ mNavSlop = (int) (16 * density);
+ DEFAULT_SCALE_PERCENT = (int) (100 * density);
+ DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
+ DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
+ mDefaultScale = density;
+ mMaxZoomScale *= scaleFactor;
+ mMinZoomScale *= scaleFactor;
+ setNewZoomScale(mActualScale * scaleFactor, false);
+ }
+ }
+
/* package */ boolean onSavePassword(String schemePlusHost, String username,
String password, final Message resumeMsg) {
boolean rVal = false;
@@ -2318,6 +2346,16 @@
}
/**
+ * Gets the chrome handler.
+ * @return the current WebChromeClient instance.
+ *
+ * @hide API council approval.
+ */
+ public WebChromeClient getWebChromeClient() {
+ return mCallbackProxy.getWebChromeClient();
+ }
+
+ /**
* Set the Picture listener. This is an interface used to receive
* notifications of a new Picture.
* @param listener An implementation of WebView.PictureListener.
@@ -3072,6 +3110,9 @@
mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
if (null == text) {
mWebTextView.setText("", 0, 0);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "rebuildWebTextView null == text");
+ }
} else {
// Change to true to enable the old style behavior, where
// entering a textfield/textarea always set the selection to the
@@ -3086,8 +3127,14 @@
} else if (isTextField) {
int length = text.length();
mWebTextView.setText(text, length, length);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "rebuildWebTextView length=" + length);
+ }
} else {
mWebTextView.setText(text, 0, 0);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "rebuildWebTextView !isTextField");
+ }
}
}
mWebTextView.requestFocus();
@@ -3129,7 +3176,7 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
- + ", " + event);
+ + ", " + event + ", unicode=" + event.getUnicodeChar());
}
if (mNativeClass == 0) {
@@ -3175,7 +3222,7 @@
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
// always handle the navigation keys in the UI thread
switchOutDrawHistory();
- if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
+ if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
playSoundEffect(keyCodeToSoundsEffect(keyCode));
return true;
}
@@ -3235,17 +3282,18 @@
}
}
- if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
+ if (nativeCursorIsPlugin()) {
+ nativeUpdatePluginReceivesEvents();
+ invalidate();
+ } else if (nativeCursorIsTextInput()) {
// This message will put the node in focus, for the DOM's notion
- // of focus
+ // of focus, and make the focuscontroller active
mWebViewCore.sendMessage(EventHub.CLICK);
- if (nativeCursorIsTextInput()) {
- // This will bring up the WebTextView and put it in focus, for
- // our view system's notion of focus
- rebuildWebTextView();
- // Now we need to pass the event to it
- return mWebTextView.onKeyDown(keyCode, event);
- }
+ // This will bring up the WebTextView and put it in focus, for
+ // our view system's notion of focus
+ rebuildWebTextView();
+ // Now we need to pass the event to it
+ return mWebTextView.onKeyDown(keyCode, event);
}
// TODO: should we pass all the keys to DOM or check the meta tag
@@ -3264,7 +3312,7 @@
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
- + ", " + event);
+ + ", " + event + ", unicode=" + event.getUnicodeChar());
}
if (mNativeClass == 0) {
@@ -4162,7 +4210,7 @@
+ " mTrackballRemainsX=" + mTrackballRemainsX
+ " mTrackballRemainsY=" + mTrackballRemainsY);
}
- if (navHandledKey(selectKeyCode, count, false, time)) {
+ if (navHandledKey(selectKeyCode, count, false, time, false)) {
playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
}
mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -4238,8 +4286,8 @@
float oldScale = mActualScale;
// snap to DEFAULT_SCALE if it is close
- if (scale > (DEFAULT_SCALE - 0.05) && scale < (DEFAULT_SCALE + 0.05)) {
- scale = DEFAULT_SCALE;
+ if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
+ scale = mDefaultScale;
}
setNewZoomScale(scale, false);
@@ -4444,7 +4492,7 @@
return result;
}
if (mNativeClass != 0 && !nativeHasCursorNode()) {
- navHandledKey(fakeKeyDirection, 1, true, 0);
+ navHandledKey(fakeKeyDirection, 1, true, 0, true);
}
}
}
@@ -4622,9 +4670,11 @@
break;
}
case SWITCH_TO_LONGPRESS: {
- mTouchMode = TOUCH_DONE_MODE;
- performLongClick();
- rebuildWebTextView();
+ if (!mPreventDrag) {
+ mTouchMode = TOUCH_DONE_MODE;
+ performLongClick();
+ rebuildWebTextView();
+ }
break;
}
case SWITCH_TO_CLICK:
@@ -4639,13 +4689,15 @@
break;
}
nativeSetFollowedLink(true);
- mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
- cursorData());
+ nativeUpdatePluginReceivesEvents();
+ WebViewCore.CursorData data = cursorData();
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
playSoundEffect(SoundEffectConstants.CLICK);
boolean isTextInput = nativeCursorIsTextInput();
if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
nativeCursorText())) {
- mWebViewCore.sendMessage(EventHub.CLICK);
+ mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
+ nativeCursorNodePointer());
}
if (isTextInput) {
rebuildWebTextView();
@@ -4769,7 +4821,7 @@
int initialScale = msg.arg1;
int viewportWidth = msg.arg2;
// start a new page with DEFAULT_SCALE zoom scale.
- float scale = DEFAULT_SCALE;
+ float scale = mDefaultScale;
if (mInitialScale > 0) {
scale = mInitialScale / 100.0f;
} else {
@@ -4791,6 +4843,11 @@
}
setNewZoomScale(scale, false);
break;
+ case MOVE_OUT_OF_PLUGIN:
+ if (nativePluginEatsNavKey()) {
+ navHandledKey(msg.arg1, 1, false, 0, true);
+ }
+ break;
case UPDATE_TEXT_ENTRY_MSG_ID:
// this is sent after finishing resize in WebViewCore. Make
// sure the text edit box is still on the screen.
@@ -4799,6 +4856,9 @@
}
rebuildWebTextView();
break;
+ case CLEAR_TEXT_ENTRY:
+ clearTextEntry();
+ break;
case INVAL_RECT_MSG_ID: {
Rect r = (Rect)msg.obj;
if (r == null) {
@@ -5118,12 +5178,23 @@
new WebViewCore.CursorData(frame, node, x, y));
}
- // called by JNI
- private void sendMoveMouseIfLatest(boolean setFocusControllerInactive) {
- if (setFocusControllerInactive) {
+ /*
+ * Send a mouse move event to the webcore thread.
+ *
+ * @param removeFocus Pass true if the "mouse" cursor is now over a node
+ * which wants key events, but it is not the focus. This
+ * will make the visual appear as though nothing is in
+ * focus. Remove the WebTextView, if present, and stop
+ * drawing the blinking caret.
+ * called by JNI
+ */
+ private void sendMoveMouseIfLatest(boolean removeFocus) {
+ if (removeFocus) {
+ clearTextEntry();
setFocusControllerInactive();
}
- mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST, cursorData());
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
+ cursorData());
}
// called by JNI
@@ -5176,11 +5247,21 @@
}
// return true if the key was handled
- private boolean navHandledKey(int keyCode, int count, boolean noScroll
- , long time) {
+ private boolean navHandledKey(int keyCode, int count, boolean noScroll,
+ long time, boolean ignorePlugin) {
if (mNativeClass == 0) {
return false;
}
+ if (ignorePlugin == false && nativePluginEatsNavKey()) {
+ KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
+ , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
+ | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
+ | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
+ , 0, 0, 0);
+ mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+ mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+ return true;
+ }
mLastCursorTime = time;
mLastCursorBounds = nativeGetCursorRingBounds();
boolean keyHandled
@@ -5263,6 +5344,7 @@
/* package */ native boolean nativeCursorMatchesFocus();
private native boolean nativeCursorIntersects(Rect visibleRect);
private native boolean nativeCursorIsAnchor();
+ private native boolean nativeCursorIsPlugin();
private native boolean nativeCursorIsTextInput();
private native Point nativeCursorPosition();
private native String nativeCursorText();
@@ -5286,7 +5368,7 @@
private native boolean nativeFocusCandidateIsTextField();
private native boolean nativeFocusCandidateIsTextInput();
private native int nativeFocusCandidateMaxLength();
- private native String nativeFocusCandidateName();
+ /* package */ native String nativeFocusCandidateName();
private native Rect nativeFocusCandidateNodeBounds();
/* package */ native int nativeFocusCandidatePointer();
private native String nativeFocusCandidateText();
@@ -5306,6 +5388,7 @@
private native int nativeMoveGeneration();
private native void nativeMoveSelection(int x, int y,
boolean extendSelection);
+ private native boolean nativePluginEatsNavKey();
// Like many other of our native methods, you must make sure that
// mNativeClass is not null before calling this method.
private native void nativeRecordButtons(boolean focused,
@@ -5319,5 +5402,5 @@
// we always want to pass in our generation number.
private native void nativeUpdateCachedTextfield(String updatedText,
int generation);
-
+ private native void nativeUpdatePluginReceivesEvents();
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4ad9a1a..b75ba941 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -136,7 +136,7 @@
// ready.
mEventHub = new EventHub();
// Create a WebSettings object for maintaining all settings
- mSettings = new WebSettings(mContext);
+ mSettings = new WebSettings(mContext, mWebView);
// The WebIconDatabase needs to be initialized within the UI thread so
// just request the instance here.
WebIconDatabase.getInstance();
@@ -346,9 +346,10 @@
private native void nativeSplitContent();
private native boolean nativeKey(int keyCode, int unichar,
- int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
+ int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
+ boolean isDown);
- private native boolean nativeClick();
+ private native void nativeClick(int framePtr, int nodePtr);
private native void nativeSendListBoxChoices(boolean[] choices, int size);
@@ -719,9 +720,11 @@
@Override
public void handleMessage(Message msg) {
if (DebugFlags.WEB_VIEW_CORE) {
- Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
+ Log.v(LOGTAG, (msg.what < LOAD_URL || msg.what
> FREE_MEMORY ? Integer.toString(msg.what)
- : HandlerDebugString[msg.what - LOAD_URL]);
+ : HandlerDebugString[msg.what - LOAD_URL])
+ + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
+ + " obj=" + msg.obj);
}
switch (msg.what) {
case WEBKIT_DRAW:
@@ -803,7 +806,7 @@
break;
case CLICK:
- nativeClick();
+ nativeClick(msg.arg1, msg.arg2);
break;
case VIEW_SIZE_CHANGED:
@@ -1260,7 +1263,19 @@
int keyCode = evt.getKeyCode();
if (!nativeKey(keyCode, evt.getUnicodeChar(),
evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
+ evt.isSymPressed(),
isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
+ if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
+ && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
+ }
+ if (mWebView != null && evt.isDown()) {
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
+ }
+ return;
+ }
// bubble up the event handling
// but do not bubble up the ENTER key, which would open the search
// bar without any text.
@@ -1618,6 +1633,20 @@
// set the viewport settings from WebKit
setViewportSettingsFromNative();
+ // adjust the default scale to match the density
+ if (WebView.DEFAULT_SCALE_PERCENT != 100) {
+ float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f;
+ if (mViewportInitialScale > 0) {
+ mViewportInitialScale *= adjust;
+ }
+ if (mViewportMinimumScale > 0) {
+ mViewportMinimumScale *= adjust;
+ }
+ if (mViewportMaximumScale > 0) {
+ mViewportMaximumScale *= adjust;
+ }
+ }
+
// infer the values if they are not defined.
if (mViewportWidth == 0) {
if (mViewportInitialScale == 0) {
@@ -1722,6 +1751,13 @@
}
}
+ // called by JNI
+ private void clearTextEntry() {
+ if (mWebView == null) return;
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.CLEAR_TEXT_ENTRY).sendToTarget();
+ }
+
// these must be in document space (i.e. not scaled/zoomed).
private native void nativeSetScrollOffset(int gen, int dx, int dy);
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 1004e30..e6d89e3 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -48,7 +48,8 @@
// 6 -> 7 Change cache localPath from int to String
// 7 -> 8 Move cache to its own db
// 8 -> 9 Store both scheme and host when storing passwords
- private static final int CACHE_DATABASE_VERSION = 1;
+ private static final int CACHE_DATABASE_VERSION = 2;
+ // 1 -> 2 Add expires String
private static WebViewDatabase mInstance = null;
@@ -107,6 +108,8 @@
private static final String CACHE_EXPIRES_COL = "expires";
+ private static final String CACHE_EXPIRES_STRING_COL = "expiresstring";
+
private static final String CACHE_MIMETYPE_COL = "mimetype";
private static final String CACHE_ENCODING_COL = "encoding";
@@ -150,6 +153,7 @@
private static int mCacheLastModifyColIndex;
private static int mCacheETagColIndex;
private static int mCacheExpiresColIndex;
+ private static int mCacheExpiresStringColIndex;
private static int mCacheMimeTypeColIndex;
private static int mCacheEncodingColIndex;
private static int mCacheHttpStatusColIndex;
@@ -220,6 +224,8 @@
.getColumnIndex(CACHE_ETAG_COL);
mCacheExpiresColIndex = mCacheInserter
.getColumnIndex(CACHE_EXPIRES_COL);
+ mCacheExpiresStringColIndex = mCacheInserter
+ .getColumnIndex(CACHE_EXPIRES_STRING_COL);
mCacheMimeTypeColIndex = mCacheInserter
.getColumnIndex(CACHE_MIMETYPE_COL);
mCacheEncodingColIndex = mCacheInserter
@@ -320,6 +326,7 @@
+ " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, "
+ CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL
+ " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, "
+ + CACHE_EXPIRES_STRING_COL + " TEXT, "
+ CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
+ " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
+ CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
@@ -537,7 +544,7 @@
}
Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
- + "mimetype, encoding, httpstatus, location, contentlength "
+ + "expiresstring, mimetype, encoding, httpstatus, location, contentlength "
+ "FROM cache WHERE url = ?",
new String[] { url });
@@ -548,11 +555,12 @@
ret.lastModified = cursor.getString(1);
ret.etag = cursor.getString(2);
ret.expires = cursor.getLong(3);
- ret.mimeType = cursor.getString(4);
- ret.encoding = cursor.getString(5);
- ret.httpStatusCode = cursor.getInt(6);
- ret.location = cursor.getString(7);
- ret.contentLength = cursor.getLong(8);
+ ret.expiresString = cursor.getString(4);
+ ret.mimeType = cursor.getString(5);
+ ret.encoding = cursor.getString(6);
+ ret.httpStatusCode = cursor.getInt(7);
+ ret.location = cursor.getString(8);
+ ret.contentLength = cursor.getLong(9);
return ret;
}
} finally {
@@ -591,6 +599,7 @@
mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
mCacheInserter.bind(mCacheETagColIndex, c.etag);
mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
+ mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
diff --git a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
index 74d27ed..b3d7f69 100644
--- a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
+++ b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
@@ -38,7 +38,6 @@
import java.io.OutputStream;
import java.io.IOException;
import java.lang.StringBuilder;
-import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
@@ -57,7 +56,6 @@
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.conn.ssl.StrictHostnameVerifier;
-import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.util.CharArrayBuffer;
import java.util.concurrent.locks.Condition;
@@ -863,12 +861,9 @@
mResponseHeaders = new HashMap<String, String[]>();
String contentLength = Long.toString(cacheResult.getContentLength());
setResponseHeader(KEY_CONTENT_LENGTH, contentLength);
- long expires = cacheResult.getExpires();
- if (expires >= 0) {
- // "Expires" header is valid and finite. Milliseconds since 1970
- // epoch, formatted as RFC-1123.
- String expiresString = DateUtils.formatDate(new Date(expires));
- setResponseHeader(KEY_EXPIRES, expiresString);
+ String expires = cacheResult.getExpiresString();
+ if (expires != null) {
+ setResponseHeader(KEY_EXPIRES, expires);
}
String lastModified = cacheResult.getLastModified();
if (lastModified != null) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index bd6edfb..0c2cd55 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -990,7 +990,7 @@
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
- bottomEdge = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight();
+ bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels;
}
final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 955475e4..12bb01c 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -41,6 +41,7 @@
import java.util.TreeSet;
import java.util.LinkedList;
import java.util.HashSet;
+import java.util.ArrayList;
/**
* A Layout where the positions of the children can be described in relation to each other or to the
@@ -339,11 +340,17 @@
int right = Integer.MIN_VALUE;
int bottom = Integer.MIN_VALUE;
+ boolean offsetHorizontalAxis = false;
+ boolean offsetVerticalAxis = false;
+
if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
ignore = findViewById(mIgnoreGravity);
}
- View[] views = mSortedVerticalChildren;
+ final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
+ final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
+
+ View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
@@ -351,13 +358,16 @@
LayoutParams params = (LayoutParams) child.getLayoutParams();
applyHorizontalSizeRules(params, myWidth);
- measureChildHorizontal(child, params, myWidth);
- positionChildHorizontal(child, params, myWidth);
+ measureChildHorizontal(child, params, myWidth, myHeight);
+ if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
+ offsetHorizontalAxis = true;
+ }
}
}
- views = mSortedHorizontalChildren;
+ views = mSortedVerticalChildren;
count = views.length;
+
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
@@ -365,12 +375,15 @@
applyVerticalSizeRules(params, myHeight);
measureChild(child, params, myWidth, myHeight);
- positionChildVertical(child, params, myHeight);
+ if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
+ offsetVerticalAxis = true;
+ }
- if (widthMode != MeasureSpec.EXACTLY) {
+ if (isWrapContentWidth) {
width = Math.max(width, params.mRight);
}
- if (heightMode != MeasureSpec.EXACTLY) {
+
+ if (isWrapContentHeight) {
height = Math.max(height, params.mBottom);
}
@@ -406,7 +419,7 @@
}
}
- if (widthMode != MeasureSpec.EXACTLY) {
+ if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
width += mPaddingRight;
@@ -417,8 +430,22 @@
width = Math.max(width, getSuggestedMinimumWidth());
width = resolveSize(width, widthMeasureSpec);
+
+ if (offsetHorizontalAxis) {
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ final int[] rules = params.getRules();
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
+ centerHorizontal(child, params, width);
+ }
+ }
+ }
+ }
}
- if (heightMode != MeasureSpec.EXACTLY) {
+
+ if (isWrapContentHeight) {
// Height already has top padding in it since it was calculated by looking at
// the bottom of each child view
height += mPaddingBottom;
@@ -429,6 +456,19 @@
height = Math.max(height, getSuggestedMinimumHeight());
height = resolveSize(height, heightMeasureSpec);
+
+ if (offsetVerticalAxis) {
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ final int[] rules = params.getRules();
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
+ centerVertical(child, params, height);
+ }
+ }
+ }
+ }
}
if (horizontalGravity || verticalGravity) {
@@ -510,13 +550,18 @@
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
- private void measureChildHorizontal(View child, LayoutParams params, int myWidth) {
+ private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
params.mRight, params.width,
params.leftMargin, params.rightMargin,
mPaddingLeft, mPaddingRight,
myWidth);
- int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ int childHeightMeasureSpec;
+ if (params.width == LayoutParams.FILL_PARENT) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, myHeight);
+ } else {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@@ -599,7 +644,9 @@
return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
}
- private void positionChildHorizontal(View child, LayoutParams params, int myWidth) {
+ private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
+ boolean wrapContent) {
+
int[] rules = params.getRules();
if (params.mLeft < 0 && params.mRight >= 0) {
@@ -610,16 +657,25 @@
params.mRight = params.mLeft + child.getMeasuredWidth();
} else if (params.mLeft < 0 && params.mRight < 0) {
// Both left and right vary
- if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) {
- centerHorizontal(child, params, myWidth);
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
+ if (!wrapContent) {
+ centerHorizontal(child, params, myWidth);
+ } else {
+ params.mLeft = mPaddingLeft + params.leftMargin;
+ params.mRight = params.mLeft + child.getMeasuredWidth();
+ }
+ return true;
} else {
params.mLeft = mPaddingLeft + params.leftMargin;
params.mRight = params.mLeft + child.getMeasuredWidth();
}
}
+ return false;
}
- private void positionChildVertical(View child, LayoutParams params, int myHeight) {
+ private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
+ boolean wrapContent) {
+
int[] rules = params.getRules();
if (params.mTop < 0 && params.mBottom >= 0) {
@@ -630,13 +686,20 @@
params.mBottom = params.mTop + child.getMeasuredHeight();
} else if (params.mTop < 0 && params.mBottom < 0) {
// Both top and bottom vary
- if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) {
- centerVertical(child, params, myHeight);
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
+ if (!wrapContent) {
+ centerVertical(child, params, myHeight);
+ } else {
+ params.mTop = mPaddingTop + params.topMargin;
+ params.mBottom = params.mTop + child.getMeasuredHeight();
+ }
+ return true;
} else {
params.mTop = mPaddingTop + params.topMargin;
params.mBottom = params.mTop + child.getMeasuredHeight();
}
}
+ return false;
}
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
@@ -766,14 +829,14 @@
private View getRelatedView(int[] rules, int relation) {
int id = rules[relation];
if (id != 0) {
- DependencyGraph.Node node = mGraph.mNodes.get(id);
+ DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
if (node == null) return null;
View v = node.view;
// Find the first non-GONE view up the chain
while (v.getVisibility() == View.GONE) {
rules = ((LayoutParams) v.getLayoutParams()).getRules();
- node = mGraph.mNodes.get((rules[relation]));
+ node = mGraph.mKeyNodes.get((rules[relation]));
if (node == null) return null;
v = node.view;
}
@@ -1109,10 +1172,15 @@
private static class DependencyGraph {
/**
+ * List of all views in the graph.
+ */
+ private ArrayList<Node> mNodes = new ArrayList<Node>();
+
+ /**
* List of nodes in the graph. Each node is identified by its
* view id (see View#getId()).
*/
- private SparseArray<Node> mNodes = new SparseArray<Node>();
+ private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
/**
* Temporary data structure used to build the list of roots
@@ -1124,14 +1192,15 @@
* Clears the graph.
*/
void clear() {
- final SparseArray<Node> nodes = mNodes;
+ final ArrayList<Node> nodes = mNodes;
final int count = nodes.size();
for (int i = 0; i < count; i++) {
- nodes.valueAt(i).release();
+ nodes.get(i).release();
}
nodes.clear();
+ mKeyNodes.clear();
mRoots.clear();
}
@@ -1141,7 +1210,14 @@
* @param view The view to be added as a node to the graph.
*/
void add(View view) {
- mNodes.put(view.getId(), Node.acquire(view));
+ final int id = view.getId();
+ final Node node = Node.acquire(view);
+
+ if (id != View.NO_ID) {
+ mKeyNodes.put(id, node);
+ }
+
+ mNodes.add(node);
}
/**
@@ -1192,20 +1268,21 @@
* @return A list of node, each being a root of the graph
*/
private LinkedList<Node> findRoots(int[] rulesFilter) {
- final SparseArray<Node> nodes = mNodes;
+ final SparseArray<Node> keyNodes = mKeyNodes;
+ final ArrayList<Node> nodes = mNodes;
final int count = nodes.size();
// Find roots can be invoked several times, so make sure to clear
// all dependents and dependencies before running the algorithm
for (int i = 0; i < count; i++) {
- final Node node = nodes.valueAt(i);
+ final Node node = nodes.get(i);
node.dependents.clear();
node.dependencies.clear();
}
// Builds up the dependents and dependencies for each node of the graph
for (int i = 0; i < count; i++) {
- final Node node = nodes.valueAt(i);
+ final Node node = nodes.get(i);
final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
final int[] rules = layoutParams.mRules;
@@ -1217,11 +1294,14 @@
final int rule = rules[rulesFilter[j]];
if (rule > 0) {
// The node this node depends on
- final Node dependency = nodes.get(rule);
+ final Node dependency = keyNodes.get(rule);
if (dependency == node) {
throw new IllegalStateException("A view cannot have a dependency" +
" on itself");
}
+ if (dependency == null) {
+ continue;
+ }
// Add the current node as a dependent
dependency.dependents.add(node);
// Add a dependency to the current node
@@ -1235,7 +1315,7 @@
// Finds all the roots in the graph: all nodes with no dependencies
for (int i = 0; i < count; i++) {
- final Node node = nodes.valueAt(i);
+ final Node node = nodes.get(i);
if (node.dependencies.size() == 0) roots.add(node);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9479f9e..d8ed4f0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3681,12 +3681,13 @@
@Override
protected boolean isPaddingOffsetRequired() {
- return mShadowRadius != 0;
+ return mShadowRadius != 0 || mDrawables != null;
}
@Override
protected int getLeftPaddingOffset() {
- return (int) Math.min(0, mShadowDx - mShadowRadius);
+ return getCompoundPaddingLeft() - mPaddingLeft +
+ (int) Math.min(0, mShadowDx - mShadowRadius);
}
@Override
@@ -3701,7 +3702,8 @@
@Override
protected int getRightPaddingOffset() {
- return (int) Math.max(0, mShadowDx + mShadowRadius);
+ return -(getCompoundPaddingRight() - mPaddingRight) +
+ (int) Math.max(0, mShadowDx + mShadowRadius);
}
@Override
@@ -6665,9 +6667,10 @@
} else if (getLineCount() == 1) {
switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
- return (mLayout.getLineRight(0) - mScrollX - (mRight - mLeft) -
- getCompoundPaddingLeft() - getCompoundPaddingRight()) /
- getHorizontalFadingEdgeLength();
+ final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() -
+ getCompoundPaddingRight();
+ final float lineWidth = mLayout.getLineWidth(0);
+ return (lineWidth - textWidth) / getHorizontalFadingEdgeLength();
case Gravity.RIGHT:
return 0.0f;
case Gravity.CENTER_HORIZONTAL:
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 84ed729..4bef265 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -41,6 +41,20 @@
- adb: close the file
*/
/**
+ * Ask the transport where, on local device storage, to keep backup state blobs.
+ * This is per-transport so that mock transports used for testing can coexist with
+ * "live" backup services without interfering with the live bookkeeping. The
+ * returned string should be a name that is expected to be unambiguous among all
+ * available backup transports; the name of the class implementing the transport
+ * is a good choice.
+ *
+ * @return A unique name, suitable for use as a file or directory name, that the
+ * Backup Manager could use to disambiguate state files associated with
+ * different backup transports.
+ */
+ String transportDirName();
+
+ /**
* Verify that this is a suitable time for a backup pass. This should return zero
* if a backup is reasonable right now, some positive value otherwise. This method
* will be called outside of the {@link #startSession}/{@link #endSession} pair.
@@ -54,63 +68,70 @@
long requestBackupTime();
/**
- * Establish a connection to the back-end data repository, if necessary. If the transport
- * needs to initialize state that is not tied to individual applications' backup operations,
- * this is where it should be done.
- *
- * @return Zero on success; a nonzero error code on failure.
- */
- int startSession();
-
- /**
- * Send one application's data to the backup destination.
+ * Send one application's data to the backup destination. The transport may send
+ * the data immediately, or may buffer it. After this is called, {@link #finishBackup}
+ * must be called to ensure the data is sent and recorded successfully.
*
* @param packageInfo The identity of the application whose data is being backed up.
* This specifically includes the signature list for the package.
* @param data The data stream that resulted from invoking the application's
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
- * @return Zero on success; a nonzero error code on failure.
+ * @return false if errors occurred (the backup should be aborted and rescheduled),
+ * true if everything is OK so far (but {@link #finishBackup} must be called).
*/
- int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor data);
+ boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);
+
+ /**
+ * Finish sending application data to the backup destination. This must be
+ * called after {@link #performBackup} to ensure that all data is sent. Only
+ * when this method returns true can the backup be assumed to have succeeded.
+ *
+ * @return false if errors occurred (the backup should be aborted and rescheduled),
+ * true if everything is OK so far (but {@link #finishBackup} must be called).
+ */
+ boolean finishBackup();
/**
* Get the set of backups currently available over this transport.
*
- * @return Descriptions of the set of restore images available for this device.
+ * @return Descriptions of the set of restore images available for this device,
+ * or null if an error occurred (the attempt should be rescheduled).
**/
RestoreSet[] getAvailableRestoreSets();
/**
- * Get the set of applications from a given restore image.
+ * Start restoring application data from backup. After calling this function,
+ * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
+ * to walk through the actual application data.
*
* @param token A backup token as returned by {@link #getAvailableRestoreSets}.
- * @return An array of PackageInfo objects describing all of the applications
- * available for restore from this restore image. This should include the list
- * of signatures for each package so that the Backup Manager can filter using that
- * information.
+ * @param packages List of applications to restore (if data is available).
+ * Application data will be restored in the order given.
+ * @return false if errors occurred (the restore should be aborted and rescheduled),
+ * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
*/
- PackageInfo[] getAppSet(int token);
+ boolean startRestore(long token, in PackageInfo[] packages);
/**
- * Retrieve one application's data from the backing store.
- *
- * @param token The backup record from which a restore is being requested.
- * @param packageInfo The identity of the application whose data is being restored.
- * This must include the signature list for the package; it is up to the transport
- * to verify that the requested app's signatures match the saved backup record
- * because the transport cannot necessarily trust the client device.
- * @param data An open, writable file into which the backup image should be stored.
- * @return Zero on success; a nonzero error code on failure.
+ * Get the package name of the next application with data in the backup store.
+ * @return The name of one of the packages supplied to {@link #startRestore},
+ * or "" (the empty string) if no more backup data is available,
+ * or null if an error occurred (the restore should be aborted and rescheduled).
*/
- int getRestoreData(int token, in PackageInfo packageInfo, in ParcelFileDescriptor data);
+ String nextRestorePackage();
/**
- * Terminate the backup session, closing files, freeing memory, and cleaning up whatever
- * other state the transport required.
- *
- * @return Zero on success; a nonzero error code on failure. Even on failure, the session
- * is torn down and must be restarted if another backup is attempted.
+ * Get the data for the application returned by {@link #nextRestorePackage}.
+ * @param data An open, writable file into which the backup data should be stored.
+ * @return false if errors occurred (the restore should be aborted and rescheduled),
+ * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
*/
- int endSession();
+ boolean getRestoreData(in ParcelFileDescriptor outFd);
+
+ /**
+ * End a restore session (aborting any in-process data transfer as necessary),
+ * freeing any resources and connections used during the restore process.
+ */
+ void finishRestore();
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 3dd71f3..c5d9d403 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -30,14 +30,14 @@
private static final String TAG = "LocalTransport";
private static final boolean DEBUG = true;
+ private static final String TRANSPORT_DIR_NAME
+ = "com.android.internal.backup.LocalTransport";
+
private Context mContext;
private PackageManager mPackageManager;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
- private FileFilter mDirFileFilter = new FileFilter() {
- public boolean accept(File f) {
- return f.isDirectory();
- }
- };
+ private PackageInfo[] mRestorePackages = null;
+ private int mRestorePackage = -1; // Index into mRestorePackages
public LocalTransport(Context context) {
@@ -46,26 +46,19 @@
mPackageManager = context.getPackageManager();
}
+
+ public String transportDirName() throws RemoteException {
+ return TRANSPORT_DIR_NAME;
+ }
+
public long requestBackupTime() throws RemoteException {
// any time is a good time for local backup
return 0;
}
- public int startSession() throws RemoteException {
- if (DEBUG) Log.v(TAG, "session started");
- mDataDir.mkdirs();
- return 0;
- }
-
- public int endSession() throws RemoteException {
- if (DEBUG) Log.v(TAG, "session ended");
- return 0;
- }
-
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+ public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
throws RemoteException {
if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
- int err = 0;
File packageDir = new File(mDataDir, packageInfo.packageName);
packageDir.mkdirs();
@@ -101,9 +94,8 @@
try {
entity.write(buf, 0, dataSize);
} catch (IOException e) {
- Log.e(TAG, "Unable to update key file "
- + entityFile.getAbsolutePath());
- err = -1;
+ Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
+ return false;
} finally {
entity.close();
}
@@ -111,14 +103,17 @@
entityFile.delete();
}
}
+ return true;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
- Log.v(TAG, "Exception reading backup input:");
- e.printStackTrace();
- err = -1;
+ Log.v(TAG, "Exception reading backup input:", e);
+ return false;
}
+ }
- return err;
+ public boolean finishBackup() throws RemoteException {
+ if (DEBUG) Log.v(TAG, "finishBackup()");
+ return true;
}
// Restore handling
@@ -129,72 +124,66 @@
return array;
}
- public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
- if (DEBUG) Log.v(TAG, "getting app set " + token);
- // the available packages are the extant subdirs of mDatadir
- File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
- ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
- for (File dir : packageDirs) {
- try {
- PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
- PackageManager.GET_SIGNATURES);
- if (pkg != null) {
- packages.add(pkg);
- }
- } catch (NameNotFoundException e) {
- // restore set contains data for a package not installed on the
- // phone -- just ignore it.
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, "Built app set of " + packages.size() + " entries:");
- for (PackageInfo p : packages) {
- Log.v(TAG, " + " + p.packageName);
- }
- }
-
- PackageInfo[] result = new PackageInfo[packages.size()];
- return packages.toArray(result);
+ public boolean startRestore(long token, PackageInfo[] packages) {
+ if (DEBUG) Log.v(TAG, "start restore " + token);
+ mRestorePackages = packages;
+ mRestorePackage = -1;
+ return true;
}
- public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
- throws android.os.RemoteException {
- if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
- // we only support one hardcoded restore set
- if (token != 0) return -1;
+ public String nextRestorePackage() {
+ if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+ while (++mRestorePackage < mRestorePackages.length) {
+ String name = mRestorePackages[mRestorePackage].packageName;
+ if (new File(mDataDir, name).isDirectory()) {
+ if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
+ return name;
+ }
+ }
- // the data for a given package is at a known location
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ if (DEBUG) Log.v(TAG, " no more packages to restore");
+ return "";
+ }
+
+ public boolean getRestoreData(ParcelFileDescriptor outFd) {
+ if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+ if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
+ File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
// The restore set is the concatenation of the individual record blobs,
// each of which is a file in the package's directory
File[] blobs = packageDir.listFiles();
- if (DEBUG) Log.v(TAG, " found " + blobs.length + " key files");
- int err = 0;
- if (blobs != null && blobs.length > 0) {
- BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
- try {
- for (File f : blobs) {
- copyToRestoreData(f, out);
- }
- } catch (Exception e) {
- Log.e(TAG, "Unable to read backup records");
- err = -1;
- }
+ if (blobs == null) {
+ Log.e(TAG, "Error listing directory: " + packageDir);
+ return false; // nextRestorePackage() ensures the dir exists, so this is an error
}
- return err;
+
+ // We expect at least some data if the directory exists in the first place
+ if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
+ BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
+ try {
+ for (File f : blobs) {
+ FileInputStream in = new FileInputStream(f);
+ try {
+ int size = (int) f.length();
+ byte[] buf = new byte[size];
+ in.read(buf);
+ String key = new String(Base64.decode(f.getName()));
+ if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
+ out.writeEntityHeader(key, size);
+ out.writeEntityData(buf, size);
+ } finally {
+ in.close();
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to read backup records", e);
+ return false;
+ }
}
- private void copyToRestoreData(File f, BackupDataOutput out) throws IOException {
- FileInputStream in = new FileInputStream(f);
- int size = (int) f.length();
- byte[] buf = new byte[size];
- in.read(buf);
- String key = new String(Base64.decode(f.getName()));
- if (DEBUG) Log.v(TAG, " ... copy to stream: key=" + key
- + " size=" + size);
- out.writeEntityHeader(key, size);
- out.writeEntityData(buf, size);
+ public void finishRestore() {
+ if (DEBUG) Log.v(TAG, "finishRestore()");
}
}
diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java
new file mode 100644
index 0000000..6b396d7
--- /dev/null
+++ b/core/java/com/android/internal/backup/SystemBackupAgent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.backup;
+
+import android.backup.AbsoluteFileBackupHelper;
+import android.backup.BackupHelperAgent;
+
+/**
+ * Backup agent for various system-managed data
+ */
+public class SystemBackupAgent extends BackupHelperAgent {
+ // the set of files that we back up whole, as absolute paths
+ String[] mFiles = {
+ /* WallpaperService.WALLPAPER_FILE */
+ "/data/data/com.android.settings/files/wallpaper",
+ };
+
+ public void onCreate() {
+ addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles));
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fc4a9c4..a03802d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2836,14 +2836,12 @@
* @param name process name
* @return the statistics object for the process
*/
- public Uid.Proc getProcessStatsLocked(String name) {
+ public Uid.Proc getProcessStatsLocked(String name, int pid) {
int uid;
if (mUidCache.containsKey(name)) {
uid = mUidCache.get(name);
} else {
- // TODO: Find the actual uid from /proc/pid/status. For now use the hashcode of the
- // process name
- uid = name.hashCode();
+ uid = Process.getUidForPid(pid);
mUidCache.put(name, uid);
}
Uid u = getUidStatsLocked(uid);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0ecf678..73eea15 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -23,6 +23,7 @@
ActivityManager.cpp \
AndroidRuntime.cpp \
CursorWindow.cpp \
+ Time.cpp \
com_google_android_gles_jni_EGLImpl.cpp \
com_google_android_gles_jni_GLImpl.cpp.arm \
android_opengl_GLES10.cpp \
@@ -121,7 +122,8 @@
com_android_internal_graphics_NativeUtils.cpp \
android_backup_BackupDataInput.cpp \
android_backup_BackupDataOutput.cpp \
- android_backup_FileBackupHelperBase.cpp
+ android_backup_FileBackupHelperBase.cpp \
+ android_backup_BackupHelperDispatcher.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f8a4df0..c322b17 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -156,6 +156,7 @@
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
+extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1241,6 +1242,7 @@
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelperBase),
+ REG_JNI(register_android_backup_BackupHelperDispatcher),
};
/*
diff --git a/libs/ui/Time.cpp b/core/jni/Time.cpp
similarity index 98%
rename from libs/ui/Time.cpp
rename to core/jni/Time.cpp
index b5539135..f3037f3 100644
--- a/libs/ui/Time.cpp
+++ b/core/jni/Time.cpp
@@ -1,4 +1,4 @@
-#include <utils/TimeUtils.h>
+#include "TimeUtils.h"
#include <stdio.h>
#include <cutils/tztime.h>
diff --git a/include/utils/TimeUtils.h b/core/jni/TimeUtils.h
similarity index 100%
rename from include/utils/TimeUtils.h
rename to core/jni/TimeUtils.h
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
new file mode 100644
index 0000000..24d529b
--- /dev/null
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BackupHelperDispatcher_native"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+
+#define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian
+
+namespace android
+{
+
+struct chunk_header_v1 {
+ int headerSize;
+ int version;
+ int dataSize; // corresponds to Header.chunkSize
+ int nameLength; // not including the NULL terminator, which is not written to the file
+};
+
+// java.io.FileDescriptor
+static jfieldID s_descriptorField = 0;
+static jfieldID s_chunkSizeField = 0;
+static jfieldID s_keyPrefixField = 0;
+
+static int
+readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+ chunk_header_v1 flattenedHeader;
+ int fd;
+ ssize_t amt;
+ String8 keyPrefix;
+ char* buf;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+
+ amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
+ if (amt != sizeof(flattenedHeader.headerSize)) {
+ return -1;
+ }
+
+ int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize);
+
+ if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) {
+ LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize);
+ if (remainingHeader > 0) {
+ lseek(fd, remainingHeader, SEEK_CUR);
+ // >0 means skip this chunk
+ return 1;
+ }
+ }
+
+ amt = read(fd, &flattenedHeader.version,
+ sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize));
+ if (amt <= 0) {
+ LOGW("Failed reading chunk header");
+ return -1;
+ }
+ remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize);
+
+ if (flattenedHeader.version != VERSION_1_HEADER) {
+ LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version,
+ flattenedHeader.headerSize);
+ if (remainingHeader > 0) {
+ lseek(fd, remainingHeader, SEEK_CUR);
+ // >0 means skip this chunk
+ return 1;
+ }
+ }
+
+ if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
+ remainingHeader < flattenedHeader.nameLength) {
+ LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader,
+ flattenedHeader.dataSize, flattenedHeader.nameLength);
+ return -1;
+ }
+
+ buf = keyPrefix.lockBuffer(flattenedHeader.nameLength);
+ if (buf == NULL) {
+ LOGW("unable to allocate %d bytes", flattenedHeader.nameLength);
+ return -1;
+ }
+
+ amt = read(fd, buf, flattenedHeader.nameLength);
+
+ keyPrefix.unlockBuffer(flattenedHeader.nameLength);
+
+ remainingHeader -= flattenedHeader.nameLength;
+
+ LOGD("remainingHeader=%d", remainingHeader);
+
+ if (remainingHeader > 0) {
+ lseek(fd, remainingHeader, SEEK_CUR);
+ }
+
+ env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
+ env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
+
+ return 0;
+}
+
+static int
+skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
+{
+ int fd;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+
+ lseek(fd, bytesToSkip, SEEK_CUR);
+
+ return 0;
+}
+
+static int
+padding_len(int len)
+{
+ len = len % 4;
+ return len == 0 ? len : 4 - len;
+}
+
+static int
+allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+ int pos;
+ jstring nameObj;
+ int nameLength;
+ int namePadding;
+ int headerSize;
+ int fd;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+
+ nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+
+ nameLength = env->GetStringUTFLength(nameObj);
+ namePadding = padding_len(nameLength);
+
+ headerSize = sizeof(chunk_header_v1) + nameLength + namePadding;
+
+ pos = lseek(fd, 0, SEEK_CUR);
+
+ lseek(fd, headerSize, SEEK_CUR);
+
+ return pos;
+}
+
+static int
+writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos)
+{
+ int err;
+ chunk_header_v1 header;
+ int fd;
+ int namePadding;
+ int prevPos;
+ jstring nameObj;
+ const char* buf;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+ prevPos = lseek(fd, 0, SEEK_CUR);
+
+ nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+ header.nameLength = env->GetStringUTFLength(nameObj);
+ namePadding = padding_len(header.nameLength);
+
+ header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding;
+ header.version = VERSION_1_HEADER;
+
+ lseek(fd, pos, SEEK_SET);
+ err = write(fd, &header, sizeof(chunk_header_v1));
+ if (err != sizeof(chunk_header_v1)) {
+ return errno;
+ }
+
+ buf = env->GetStringUTFChars(nameObj, NULL);
+ err = write(fd, buf, header.nameLength);
+ env->ReleaseStringUTFChars(nameObj, buf);
+ if (err != header.nameLength) {
+ return errno;
+ }
+
+ if (namePadding != 0) {
+ int zero = 0;
+ err = write(fd, &zero, namePadding);
+ if (err != namePadding) {
+ return errno;
+ }
+ }
+
+ lseek(fd, prevPos, SEEK_SET);
+ return 0;
+}
+
+static const JNINativeMethod g_methods[] = {
+ { "readHeader_native",
+ "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+ (void*)readHeader_native },
+ { "skipChunk_native",
+ "(Ljava/io/FileDescriptor;I)I",
+ (void*)skipChunk_native },
+ { "allocateHeader_native",
+ "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+ (void*)allocateHeader_native },
+ { "writeHeader_native",
+ "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
+ (void*)writeHeader_native },
+};
+
+int register_android_backup_BackupHelperDispatcher(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("java/io/FileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+ s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
+ LOG_FATAL_IF(s_descriptorField == NULL,
+ "Unable to find descriptor field in java.io.FileDescriptor");
+
+ clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header");
+ LOG_FATAL_IF(clazz == NULL,
+ "Unable to find class android.backup.BackupHelperDispatcher.Header");
+ s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
+ LOG_FATAL_IF(s_chunkSizeField == NULL,
+ "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header");
+ s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
+ LOG_FATAL_IF(s_keyPrefixField == NULL,
+ "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher",
+ g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index 7c208e9..98f4e03 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -23,7 +23,7 @@
#include "jni.h"
#include "utils/misc.h"
#include "android_runtime/AndroidRuntime.h"
-#include <utils/TimeUtils.h>
+#include "TimeUtils.h"
#include <nativehelper/JNIHelp.h>
#include <cutils/tztime.h>
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 1c9ee7d..770c755 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -269,9 +269,9 @@
void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
jint pid, jint pri)
{
- if (pri == ANDROID_PRIORITY_BACKGROUND) {
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
- } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+ } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
}
@@ -496,7 +496,7 @@
const String8& field = fields[i];
if (strncmp(p, field.string(), field.length()) == 0) {
p += field.length();
- while (*p == ' ') p++;
+ while (*p == ' ' || *p == '\t') p++;
char* num = p;
while (*p >= '0' && *p <= '9') p++;
skipToEol = *p != '\n';
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0c90769..db31818 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1000,6 +1000,7 @@
android:hasCode="false"
android:label="@string/android_system_label"
android:allowClearUserData="false"
+ android:backupAgent="com.android.internal.backup.SystemBackupAgent"
android:icon="@drawable/ic_launcher_android">
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.Dialog.Alert"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 88464f7..612e6f4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1325,7 +1325,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent"><xliff:g id="x">Mozilla/5.0 (Linux; U; Android %s)
- AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1</xliff:g></string>
+ AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17</xliff:g></string>
<!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
<string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 8486532..ca50a5e 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -69,6 +69,14 @@
TTS_MISSING_RESOURCES = -6
};
+enum tts_support_result {
+ TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+ TTS_LANG_COUNTRY_AVAILABLE = 1,
+ TTS_LANG_AVAILABLE = 0,
+ TTS_LANG_MISSING_DATA = -1,
+ TTS_LANG_NOT_SUPPORTED = -2
+};
+
class TtsEngine
{
public:
@@ -86,19 +94,32 @@
// @return TTS_SUCCESS, or TTS_FAILURE
virtual tts_result stop();
+ // Returns the level of support for the language, country and variant.
+ // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
+ // and the corresponding resources are correctly installed
+ // TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
+ // corresponding resources are correctly installed, but there is no match for
+ // the specified variant
+ // TTS_LANG_AVAILABLE if the language is supported and the
+ // corresponding resources are correctly installed, but there is no match for
+ // the specified country and variant
+ // TTS_LANG_MISSING_DATA if the required resources to provide any level of support
+ // for the language are not correctly installed
+ // TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
+ virtual tts_support_result isLanguageAvailable(const char *lang, const char *country,
+ const char *variant);
+
// Load the resources associated with the specified language. The loaded
// language will only be used once a call to setLanguage() with the same
- // language value is issued. Language values are based on the Android
- // conventions for localization as described in the Android platform
- // documentation on internationalization. This implies that language
- // data is specified in the format xx-rYY, where xx is a two letter
- // ISO 639-1 language code in lowercase and rYY is a two letter
- // ISO 3166-1-alpha-2 language code in uppercase preceded by a
- // lowercase "r".
- // @param value pointer to the language value
- // @param size length of the language value
+ // language value is issued. Language and country values are coded according to the ISO three
+ // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+ // instance. The variant value is encoded as the variant string retrieved from a
+ // java.util.Locale instance built with that variant data.
+ // @param lang pointer to the ISO three letter code for the language
+ // @param country pointer to the ISO three letter code for the country
+ // @param variant pointer to the variant code
// @return TTS_SUCCESS, or TTS_FAILURE
- virtual tts_result loadLanguage(const char *value, const size_t size);
+ virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
// Load the resources associated with the specified language, country and Locale variant.
// The loaded language will only be used once a call to setLanguageFromLocale() with the same
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index c6fd070..3952c0e 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -24,8 +24,7 @@
Region.cpp \
Surface.cpp \
SurfaceComposerClient.cpp \
- SurfaceFlingerSynchro.cpp \
- Time.cpp
+ SurfaceFlingerSynchro.cpp
LOCAL_SHARED_LIBRARIES := \
libcorecg \
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index edd1ea0..883e5f5 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.Config;
import android.util.Log;
import android.util.SparseIntArray;
@@ -183,7 +184,7 @@
// number of fixes we have received since we started navigating
private int mFixCount;
- private int mPositionMode = GPS_POSITION_MODE_STANDALONE;
+ private boolean mAgpsConfigured;
// true if we started navigation
private boolean mStarted;
@@ -355,8 +356,7 @@
try {
int port = Integer.parseInt(portString);
native_set_agps_server(AGPS_TYPE_SUPL, host, port);
- // use MS-Based position mode if SUPL support is enabled
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ mAgpsConfigured = true;
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
}
@@ -368,8 +368,7 @@
try {
int port = Integer.parseInt(portString);
native_set_agps_server(AGPS_TYPE_C2K, host, port);
- // use MS-Based position mode if SUPL support is enabled
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ mAgpsConfigured = true;
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse C2K_PORT: " + portString);
}
@@ -719,7 +718,15 @@
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
- if (!native_start(mPositionMode, false, mFixInterval)) {
+ int positionMode;
+ if (mAgpsConfigured && Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ASSISTED_GPS_ENABLED, 0) != 0) {
+ positionMode = GPS_POSITION_MODE_MS_BASED;
+ } else {
+ positionMode = GPS_POSITION_MODE_STANDALONE;
+ }
+
+ if (!native_start(positionMode, false, mFixInterval)) {
mStarted = false;
Log.e(TAG, "native_start failed in startNavigating()");
return;
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 25e31ee..de323b3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1052,23 +1052,25 @@
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
+ EGLSurface impl_draw = EGL_NO_SURFACE;
+ EGLSurface impl_read = EGL_NO_SURFACE;
egl_context_t * const c = get_context(ctx);
if (draw != EGL_NO_SURFACE) {
egl_surface_t const * d = get_surface(draw);
if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (d->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
- draw = d->surface;
+ impl_draw = d->surface;
}
if (read != EGL_NO_SURFACE) {
egl_surface_t const * r = get_surface(read);
if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (r->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
- read = r->surface;
+ impl_read = r->surface;
}
EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
- dp->dpys[c->impl], draw, read, c->context);
+ dp->dpys[c->impl], impl_draw, impl_read, c->context);
if (result == EGL_TRUE) {
setGlThreadSpecific(c->cnx->hooks);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c283418..87f4c40 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -36,6 +36,7 @@
user opt-in via Setup Wizard or Settings.
-->
<string name="def_location_providers_allowed">gps</string>
+ <bool name="assisted_gps_enabled">true</bool>
<!-- 0 == mobile, 1 == wifi. -->
<integer name="def_network_preference">1</integer>
<bool name="def_usb_mass_storage_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index f861400..602f3e1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -64,7 +64,7 @@
private static final String TAG = "SettingsProvider";
private static final String DATABASE_NAME = "settings.db";
- private static final int DATABASE_VERSION = 34;
+ private static final int DATABASE_VERSION = 35;
private Context mContext;
@@ -386,6 +386,21 @@
upgradeVersion = 34;
}
+ if (upgradeVersion == 34) {
+ db.beginTransaction();
+ try {
+ String value =
+ mContext.getResources().getBoolean(R.bool.assisted_gps_enabled) ? "1" : "0";
+ db.execSQL("INSERT OR IGNORE INTO secure(name,value) values('" +
+ Settings.Secure.ASSISTED_GPS_ENABLED + "','" + value + "');");
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ upgradeVersion = 35;
+ }
+
if (upgradeVersion != currentVersion) {
Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
+ ", must wipe the settings provider");
@@ -592,6 +607,21 @@
loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
R.integer.def_screen_off_timeout);
+ // Set default cdma emergency tone
+ loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0);
+
+ // Set default cdma call auto retry
+ loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0);
+
+ // Set default cdma DTMF type
+ loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);
+
+ // Set default hearing aid
+ loadSetting(stmt, Settings.System.HEARING_AID, 0);
+
+ // Set default tty mode
+ loadSetting(stmt, Settings.System.TTY_MODE, 0);
+
loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
R.bool.def_airplane_mode_on);
@@ -638,6 +668,9 @@
loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
R.string.def_location_providers_allowed);
+ loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
+ R.bool.assisted_gps_enabled);
+
loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
R.integer.def_network_preference);
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 0dafcc1..8537cae 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -204,11 +204,19 @@
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
}
}
- // TODO update to call back into the SynthProxy class through the
+ // Future update:
+ // For sync points in the speech, call back into the SynthProxy class through the
// javaTTSFields.synthProxyMethodPost methode to notify
- // playback has completed if the synthesis is done, i.e.
- // if status == TTS_SYNTH_DONE
- //delete pForAfter;
+ // playback has completed if the synthesis is done or if a marker has been reached.
+
+ if (status == TTS_SYNTH_DONE) {
+ // this struct was allocated in the original android_tts_SynthProxy_speak call,
+ // all processing matching this call is now done.
+ LOGV("Speech synthesis done.");
+ delete pForAfter;
+ pForAfter = NULL;
+ return TTS_CALLBACK_HALT;
+ }
// we don't update the wav (output) parameter as we'll let the next callback
// write at the same location, we've consumed the data already, but we need
@@ -289,8 +297,32 @@
variantNativeString);
}
env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(language, countryNativeString);
- env->ReleaseStringUTFChars(language, variantNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ if (jniData == 0) {
+ LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
+ return;
+ }
+
+ SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ const char *langNativeString = env->GetStringUTFChars(language, 0);
+ const char *countryNativeString = env->GetStringUTFChars(country, 0);
+ const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+ // TODO check return codes
+ if (pSynthData->mNativeSynthInterface) {
+ pSynthData->mNativeSynthInterface->loadLanguage(langNativeString, countryNativeString,
+ variantNativeString);
+ }
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
}
@@ -559,6 +591,10 @@
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
(void*)android_tts_SynthProxy_setLanguage
},
+ { "native_loadLanguage",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ (void*)android_tts_SynthProxy_loadLanguage
+ },
{ "native_setSpeechRate",
"(II)V",
(void*)android_tts_SynthProxy_setSpeechRate
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 3bdff37..a8eaaa43 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -73,6 +73,13 @@
public void setLanguage(String language, String country, String variant) {
native_setLanguage(mJniData, language, country, variant);
}
+
+ /**
+ * Loads the language: it's not set, but prepared for use later.
+ */
+ public void loadLanguage(String language, String country, String variant) {
+ native_loadLanguage(mJniData, language, country, variant);
+ }
/**
* Sets the speech rate
@@ -149,6 +156,9 @@
private native final void native_setLanguage(int jniData, String language, String country,
String variant);
+
+ private native final void native_loadLanguage(int jniData, String language, String country,
+ String variant);
private native final void native_setSpeechRate(int jniData, int speechRate);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 6fa3141..3c931b8 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -89,6 +89,8 @@
}
}
+ private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+
private static final String ACTION = "android.intent.action.USE_TTS";
private static final String CATEGORY = "android.intent.category.TTS";
private static final String PKGNAME = "android.tts";
@@ -108,7 +110,6 @@
private final ReentrantLock synthesizerLock = new ReentrantLock();
private SynthProxy nativeSynth;
-
@Override
public void onCreate() {
super.onCreate();
@@ -145,13 +146,11 @@
private void setDefaultSettings() {
-
// TODO handle default language
setLanguage("eng", "USA", "");
// speech rate
setSpeechRate(getDefaultRate());
-
}
@@ -424,6 +423,11 @@
return sr;
}
+ private void broadcastTtsQueueProcessingCompleted(){
+ Intent i = new Intent(Intent.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+ sendBroadcast(i);
+ }
+
private void dispatchSpeechCompletedCallbacks(String mark) {
Log.i("TTS callback", "dispatch started");
// Broadcast to all clients the new value.
@@ -440,6 +444,33 @@
Log.i("TTS callback", "dispatch completed to " + N);
}
+ private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
+ if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
+ return currentSpeechItem;
+ } else {
+ ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
+ int start = 0;
+ int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+ String splitText;
+ SpeechItem splitItem;
+ while (end < currentSpeechItem.mText.length()){
+ splitText = currentSpeechItem.mText.substring(start, end);
+ splitItem = new SpeechItem(splitText, null, SpeechItem.SPEECH);
+ splitItems.add(splitItem);
+ start = end;
+ end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+ }
+ splitText = currentSpeechItem.mText.substring(start);
+ splitItem = new SpeechItem(splitText, null, SpeechItem.SPEECH);
+ splitItems.add(splitItem);
+ mSpeechQueue.remove(0);
+ for (int i = splitItems.size() - 1; i >= 0; i--){
+ mSpeechQueue.add(0, splitItems.get(i));
+ }
+ return mSpeechQueue.get(0);
+ }
+ }
+
private void processSpeechQueue() {
boolean speechQueueAvailable = false;
try {
@@ -449,11 +480,7 @@
}
if (mSpeechQueue.size() < 1) {
mIsSpeaking = false;
- // Dispatch a completion here as this is the
- // only place where speech completes normally.
- // Nothing left to say in the queue is a special case
- // that is always a "mark" - associated text is null.
- dispatchSpeechCompletedCallbacks("");
+ broadcastTtsQueueProcessingCompleted();
return;
}
@@ -465,8 +492,7 @@
Log.i("TTS processing: ", currentSpeechItem.mText);
if (sr == null) {
if (currentSpeechItem.mType == SpeechItem.SPEECH) {
- // TODO: Split text up into smaller chunks before accepting
- // them for processing.
+ currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
speakInternalOnly(currentSpeechItem.mText,
currentSpeechItem.mParams);
} else {
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index a12db8c..2ad218f 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 8358c5c..877fa6b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -25,8 +25,7 @@
* The service that manages the L2TP-over-IPSec VPN connection.
*/
class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
- private static final String IPSEC_SERVICE = "racoon";
- private static final String L2TP_SERVICE = "mtpd";
+ private static final String IPSEC_DAEMON = "racoon";
@Override
protected void connect(String serverIp, String username, String password)
@@ -34,7 +33,7 @@
String hostIp = getHostIp();
// IPSEC
- AndroidServiceProxy ipsecService = startService(IPSEC_SERVICE);
+ AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
ipsecService.sendCommand(
String.format("SETKEY %s %s", hostIp, serverIp));
ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
@@ -42,11 +41,11 @@
getUserkeyPath()));
// L2TP
- AndroidServiceProxy l2tpService = startService(L2TP_SERVICE);
- l2tpService.sendCommand2("l2tp", serverIp, "1701", "",
- "file", getPppOptionFilePath(),
- "name", username,
- "password", password);
+ L2tpIpsecProfile p = getProfile();
+ MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
+ L2tpService.L2TP_PORT,
+ (p.isSecretEnabled() ? p.getSecretString() : null),
+ username, password);
}
private String getCaCertPath() {
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
index 9aad7a1..9273f35 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -24,19 +24,15 @@
* The service that manages the L2TP VPN connection.
*/
class L2tpService extends VpnService<L2tpProfile> {
- private static final String L2TP_SERVICE = "mtpd";
+ static final String L2TP_DAEMON = "l2tp";
+ static final String L2TP_PORT = "1701";
@Override
protected void connect(String serverIp, String username, String password)
throws IOException {
- String hostIp = getHostIp();
-
- // L2TP
- AndroidServiceProxy l2tpService = startService(L2TP_SERVICE);
- l2tpService.sendCommand2("l2tp", serverIp, "1701", "",
- "file", getPppOptionFilePath(),
- "name", username,
- "password", password);
+ L2tpProfile p = getProfile();
+ MtpdHelper.sendCommand(this, L2TP_DAEMON, serverIp, L2TP_PORT,
+ (p.isSecretEnabled() ? p.getSecretString() : null),
+ username, password);
}
-
}
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
new file mode 100644
index 0000000..6160900
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vpn;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A helper class for sending commands to the MTP daemon (mtpd).
+ */
+class MtpdHelper {
+ private static final String MTPD_SERVICE = "mtpd";
+ private static final String VPN_LINKNAME = "vpn";
+ private static final String PPP_ARGS_SEPARATOR = "";
+
+ static void sendCommand(VpnService<?> vpnService, String protocol,
+ String serverIp, String port, String secret, String username,
+ String password) throws IOException {
+ ArrayList<String> args = new ArrayList<String>();
+ args.addAll(Arrays.asList(protocol, serverIp, port));
+ if (secret != null) args.add(secret);
+ args.add(PPP_ARGS_SEPARATOR);
+ addPppArguments(vpnService, args, serverIp, username, password);
+
+ AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
+ mtpd.sendCommand2(args.toArray(new String[args.size()]));
+ }
+
+ private static void addPppArguments(VpnService<?> vpnService,
+ ArrayList<String> args, String serverIp, String username,
+ String password) throws IOException {
+ args.addAll(Arrays.asList(
+ "linkname", VPN_LINKNAME,
+ "name", username,
+ "password", password,
+ "ipparam", serverIp + "@" + vpnService.getGatewayIp(),
+ "refuse-eap", "nodefaultroute", "usepeerdns",
+ "idle", "1800",
+ "mtu", "1400",
+ "mru", "1400"));
+ }
+
+ private MtpdHelper() {
+ }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
index f20d85f..f0bbc34 100644
--- a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/PptpService.java b/packages/VpnServices/src/com/android/server/vpn/PptpService.java
new file mode 100644
index 0000000..01362a5
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/PptpService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vpn;
+
+import android.net.vpn.PptpProfile;
+
+import java.io.IOException;
+
+/**
+ * The service that manages the PPTP VPN connection.
+ */
+class PptpService extends VpnService<PptpProfile> {
+ static final String PPTP_DAEMON = "pptp";
+ static final String PPTP_PORT = "1723";
+ @Override
+ protected void connect(String serverIp, String username, String password)
+ throws IOException {
+ MtpdHelper.sendCommand(this, PPTP_DAEMON, serverIp, PPTP_PORT, null,
+ username, password);
+ }
+
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
index 14d7521..50fbf4b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index fdefe9c..44127ff 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -20,6 +20,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
+import android.net.NetworkUtils;
import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
@@ -31,8 +32,10 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.List;
/**
@@ -153,16 +156,25 @@
}
/**
- * Returns the IP of the specified host name.
+ * Returns the IP address of the specified host name.
*/
protected String getIp(String hostName) throws IOException {
- InetAddress iaddr = InetAddress.getByName(hostName);
- byte[] aa = iaddr.getAddress();
- StringBuilder sb = new StringBuilder().append(byteToInt(aa[0]));
- for (int i = 1; i < aa.length; i++) {
- sb.append(".").append(byteToInt(aa[i]));
+ return InetAddress.getByName(hostName).getHostAddress();
+ }
+
+ /**
+ * Returns the IP address of the default gateway.
+ */
+ protected String getGatewayIp() throws IOException {
+ Enumeration<NetworkInterface> ifces =
+ NetworkInterface.getNetworkInterfaces();
+ for (; ifces.hasMoreElements(); ) {
+ NetworkInterface ni = ifces.nextElement();
+ int gateway = NetworkUtils.getDefaultRoute(ni.getName());
+ if (gateway == 0) continue;
+ return toInetAddress(gateway).getHostAddress();
}
- return sb.toString();
+ throw new IOException("Default gateway is not available");
}
/**
@@ -170,7 +182,7 @@
* connection is established.
*/
protected String getConnectMonitorFile() {
- return "/etc/ppp/ip-up";
+ return "/etc/ppp/ip-up-vpn";
}
/**
@@ -461,12 +473,18 @@
}
private String reallyGetHostIp() throws IOException {
- Socket s = new Socket();
- s.connect(new InetSocketAddress("www.google.com", 80), DNS_TIMEOUT);
- String ipAddress = s.getLocalAddress().getHostAddress();
- Log.d(TAG, "Host IP: " + ipAddress);
- s.close();
- return ipAddress;
+ Enumeration<NetworkInterface> ifces =
+ NetworkInterface.getNetworkInterfaces();
+ for (; ifces.hasMoreElements(); ) {
+ NetworkInterface ni = ifces.nextElement();
+ int gateway = NetworkUtils.getDefaultRoute(ni.getName());
+ if (gateway == 0) continue;
+ Enumeration<InetAddress> addrs = ni.getInetAddresses();
+ for (; addrs.hasMoreElements(); ) {
+ return addrs.nextElement().getHostAddress();
+ }
+ }
+ throw new IOException("Host IP is not available");
}
private String getProfileSubpath(String subpath) throws IOException {
@@ -496,8 +514,13 @@
return ((message == null) || (message.length() == 0));
}
- private static int byteToInt(byte b) {
- return ((int) b) & 0x0FF;
+ private InetAddress toInetAddress(int addr) throws IOException {
+ byte[] aa = new byte[4];
+ for (int i= 0; i < aa.length; i++) {
+ aa[i] = (byte) (addr & 0x0FF);
+ addr >>= 8;
+ }
+ return InetAddress.getByAddress(aa);
}
private class ServiceHelper implements ProcessProxy.Callback {
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index 7195ea61..63fc858 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 197404e..bc2eaed 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -47,6 +47,7 @@
import android.util.SparseArray;
import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
import android.backup.IRestoreSession;
import android.backup.BackupManager;
import android.backup.RestoreSet;
@@ -76,12 +77,12 @@
private static final boolean DEBUG = true;
// Default time to wait after data changes before we back up the data
- private static final long COLLECTION_INTERVAL = 1000;
- //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+ private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
private static final int MSG_RUN_RESTORE = 3;
+ private static final String RESTORE_OBSERVER_KEY = "_resOb";
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -110,8 +111,8 @@
// Backups that we haven't started yet.
private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
= new HashMap<ApplicationInfo,BackupRequest>();
- // Do we need to back up the package manager metadata on the next pass?
- private boolean mDoPackageManager;
+
+ // Pseudoname that we use for the Package Manager metadata "package"
private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// locking around the pending-backup management
@@ -134,7 +135,18 @@
private IBackupTransport mLocalTransport, mGoogleTransport;
private RestoreSession mActiveRestoreSession;
- private File mStateDir;
+ private class RestoreParams {
+ public IBackupTransport transport;
+ public IRestoreObserver observer;
+
+ RestoreParams(IBackupTransport _transport, IRestoreObserver _obs) {
+ transport = _transport;
+ observer = _obs;
+ }
+ }
+
+ // Where we keep our journal files and other bookkeeping
+ private File mBaseStateDir;
private File mDataDir;
private File mJournalDir;
private File mJournal;
@@ -146,13 +158,12 @@
mActivityManager = ActivityManagerNative.getDefault();
// Set up our bookkeeping
- mStateDir = new File(Environment.getDataDirectory(), "backup");
- mStateDir.mkdirs();
+ mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mDataDir = Environment.getDownloadCacheDirectory();
// Set up the backup-request journaling
- mJournalDir = new File(mStateDir, "pending");
- mJournalDir.mkdirs();
+ mJournalDir = new File(mBaseStateDir, "pending");
+ mJournalDir.mkdirs(); // creates mBaseStateDir along the way
makeJournalLocked(); // okay because no other threads are running yet
// Build our mapping of uid to backup client services. This implicitly
@@ -165,8 +176,7 @@
// Set up our transport options and initialize the default transport
// TODO: Have transports register themselves somehow?
// TODO: Don't create transports that we don't need to?
- mTransportId = BackupManager.TRANSPORT_LOCAL;
- //mTransportId = BackupManager.TRANSPORT_GOOGLE;
+ mTransportId = BackupManager.TRANSPORT_GOOGLE;
mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
mGoogleTransport = null;
@@ -338,8 +348,8 @@
case MSG_RUN_RESTORE:
{
int token = msg.arg1;
- IBackupTransport transport = (IBackupTransport)msg.obj;
- (new PerformRestoreThread(transport, token)).start();
+ RestoreParams params = (RestoreParams)msg.obj;
+ (new PerformRestoreThread(params.transport, params.observer, token)).start();
break;
}
}
@@ -351,29 +361,29 @@
void addPackageParticipantsLocked(String packageName) {
// Look for apps that define the android:backupAgent attribute
if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
- List<ApplicationInfo> targetApps = allAgentApps();
+ List<PackageInfo> targetApps = allAgentPackages();
addPackageParticipantsLockedInner(packageName, targetApps);
}
private void addPackageParticipantsLockedInner(String packageName,
- List<ApplicationInfo> targetApps) {
+ List<PackageInfo> targetPkgs) {
if (DEBUG) {
- Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
- for (ApplicationInfo a : targetApps) {
- Log.v(TAG, " " + a + " agent=" + a.backupAgentName);
+ Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
+ for (PackageInfo p : targetPkgs) {
+ Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
+ + " uid=" + p.applicationInfo.uid);
}
}
- for (ApplicationInfo app : targetApps) {
- if (packageName == null || app.packageName.equals(packageName)) {
- int uid = app.uid;
+ for (PackageInfo pkg : targetPkgs) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set == null) {
set = new HashSet<ApplicationInfo>();
mBackupParticipants.put(uid, set);
}
- set.add(app);
- backUpPackageManagerData();
+ set.add(pkg.applicationInfo);
}
}
}
@@ -382,67 +392,66 @@
// 'packageName' is null, *all* participating apps will be removed.
void removePackageParticipantsLocked(String packageName) {
if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
- List<ApplicationInfo> allApps = null;
+ List<PackageInfo> allApps = null;
if (packageName != null) {
- allApps = new ArrayList<ApplicationInfo>();
+ allApps = new ArrayList<PackageInfo>();
try {
- ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
- allApps.add(app);
+ int flags = PackageManager.GET_SIGNATURES;
+ allApps.add(mPackageManager.getPackageInfo(packageName, flags));
} catch (Exception e) {
- // just skip it
+ // just skip it (???)
}
} else {
// all apps with agents
- allApps = allAgentApps();
+ allApps = allAgentPackages();
}
removePackageParticipantsLockedInner(packageName, allApps);
}
private void removePackageParticipantsLockedInner(String packageName,
- List<ApplicationInfo> agents) {
+ List<PackageInfo> agents) {
if (DEBUG) {
Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
+ ") removing " + agents.size() + " entries");
- for (ApplicationInfo a : agents) {
- Log.v(TAG, " - " + a);
+ for (PackageInfo p : agents) {
+ Log.v(TAG, " - " + p);
}
}
- for (ApplicationInfo app : agents) {
- if (packageName == null || app.packageName.equals(packageName)) {
- int uid = app.uid;
+ for (PackageInfo pkg : agents) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set != null) {
// Find the existing entry with the same package name, and remove it.
// We can't just remove(app) because the instances are different.
for (ApplicationInfo entry: set) {
- if (entry.packageName.equals(app.packageName)) {
+ if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
- backUpPackageManagerData();
break;
}
}
if (set.size() == 0) {
- mBackupParticipants.delete(uid); }
+ mBackupParticipants.delete(uid);
+ }
}
}
}
}
// Returns the set of all applications that define an android:backupAgent attribute
- private List<ApplicationInfo> allAgentApps() {
+ private List<PackageInfo> allAgentPackages() {
// !!! TODO: cache this and regenerate only when necessary
- List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
- int N = allApps.size();
- if (N > 0) {
- for (int a = N-1; a >= 0; a--) {
- ApplicationInfo app = allApps.get(a);
- if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null) {
- allApps.remove(a);
- }
+ int flags = PackageManager.GET_SIGNATURES;
+ List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+ int N = packages.size();
+ for (int a = N-1; a >= 0; a--) {
+ ApplicationInfo app = packages.get(a).applicationInfo;
+ if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+ || app.backupAgentName == null) {
+ packages.remove(a);
}
}
- return allApps;
+ return packages;
}
// Reset the given package's known backup participants. Unlike add/remove, the update
@@ -455,19 +464,11 @@
if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
// brute force but small code size
- List<ApplicationInfo> allApps = allAgentApps();
+ List<PackageInfo> allApps = allAgentPackages();
removePackageParticipantsLockedInner(packageName, allApps);
addPackageParticipantsLockedInner(packageName, allApps);
}
- private void backUpPackageManagerData() {
- // No need to schedule a backup just for the metadata; just piggyback on
- // the next actual data backup.
- synchronized(this) {
- mDoPackageManager = true;
- }
- }
-
// The queue lock should be held when scheduling a backup pass
private void scheduleBackupPassLocked(long timeFromNowMillis) {
mBackupHandler.removeMessages(MSG_RUN_BACKUP);
@@ -565,6 +566,7 @@
private static final String TAG = "PerformBackupThread";
IBackupTransport mTransport;
ArrayList<BackupRequest> mQueue;
+ File mStateDir;
File mJournal;
public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
@@ -572,52 +574,43 @@
mTransport = transport;
mQueue = queue;
mJournal = journal;
+
+ try {
+ mStateDir = new File(mBaseStateDir, transport.transportDirName());
+ } catch (RemoteException e) {
+ // can't happen; the transport is local
+ }
+ mStateDir.mkdirs();
}
@Override
public void run() {
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
- // start up the transport
- try {
- mTransport.startSession();
- } catch (Exception e) {
- Log.e(TAG, "Error session transport");
- e.printStackTrace();
- return;
- }
-
- // The transport is up and running. First, back up the package manager
- // metadata if necessary
- boolean doPackageManager;
- synchronized (BackupManagerService.this) {
- doPackageManager = mDoPackageManager;
- mDoPackageManager = false;
- }
- if (doPackageManager) {
- // The package manager doesn't have a proper <application> etc, but since
- // it's running here in the system process we can just set up its agent
- // directly and use a synthetic BackupRequest.
- if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
-
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentApps());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
- pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
- processOneBackup(pmRequest,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()),
- mTransport);
- }
+ // The package manager doesn't have a proper <application> etc, but since
+ // it's running here in the system process we can just set up its agent
+ // directly and use a synthetic BackupRequest. We always run this pass
+ // because it's cheap and this way we guarantee that we don't get out of
+ // step even if we're selecting among various transports at run time.
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, allAgentPackages());
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+ processOneBackup(pmRequest,
+ IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+ mTransport);
// Now run all the backups in our queue
doQueuedBackups(mTransport);
// Finally, tear down the transport
try {
- mTransport.endSession();
- } catch (Exception e) {
- Log.e(TAG, "Error ending transport");
- e.printStackTrace();
+ if (!mTransport.finishBackup()) {
+ // STOPSHIP TODO: handle errors
+ Log.e(TAG, "Backup failure in finishBackup()");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in finishBackup()", e);
}
if (!mJournal.delete()) {
@@ -712,24 +705,18 @@
if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
backupData =
ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
- int error = transport.performBackup(packInfo, backupData);
+ if (!transport.performBackup(packInfo, backupData)) {
+ // STOPSHIP TODO: handle errors
+ Log.e(TAG, "Backup failure in performBackup()");
+ }
// !!! TODO: After successful transport, delete the now-stale data
// and juggle the files so that next time the new state is passed
//backupDataName.delete();
newStateName.renameTo(savedStateName);
}
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Package not found on backup: " + packageName);
- } catch (FileNotFoundException fnf) {
- Log.w(TAG, "File not found on backup: ");
- fnf.printStackTrace();
- } catch (RemoteException e) {
- Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
- e.printStackTrace();
} catch (Exception e) {
- Log.w(TAG, "Final exception guard in backup: ");
- e.printStackTrace();
+ Log.e(TAG, "Error backing up " + packageName, e);
}
}
}
@@ -737,25 +724,6 @@
// ----- Restore handling -----
- // Is the given package restorable on this device? Returns the on-device app's
- // ApplicationInfo struct if it is; null if not.
- //
- // !!! TODO: also consider signatures
- PackageInfo isRestorable(PackageInfo packageInfo) {
- if (packageInfo.packageName != null) {
- try {
- PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
- PackageManager.GET_SIGNATURES);
- if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
- return app;
- }
- } catch (Exception e) {
- // doesn't exist on this device, or other error -- just ignore it.
- }
- }
- return null;
- }
-
private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
@@ -792,8 +760,10 @@
class PerformRestoreThread extends Thread {
private IBackupTransport mTransport;
+ private IRestoreObserver mObserver;
private int mToken;
private RestoreSet mImage;
+ private File mStateDir;
class RestoreRequest {
public PackageInfo app;
@@ -805,9 +775,18 @@
}
}
- PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
+ PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+ int restoreSetToken) {
mTransport = transport;
+ mObserver = observer;
mToken = restoreSetToken;
+
+ try {
+ mStateDir = new File(mBaseStateDir, transport.transportDirName());
+ } catch (RemoteException e) {
+ // can't happen; the transport is local
+ }
+ mStateDir.mkdirs();
}
@Override
@@ -816,130 +795,177 @@
/**
* Restore sequence:
*
- * 1. start up the transport session
- * 2. get the restore set description for our identity
- * 3. for each app in the restore set:
+ * 1. get the restore set description for our identity
+ * 2. for each app in the restore set:
* 3.a. if it's restorable on this device, add it to the restore queue
- * 4. for each app in the restore queue:
- * 4.a. clear the app data
- * 4.b. get the restore data for the app from the transport
- * 4.c. launch the backup agent for the app
- * 4.d. agent.doRestore() with the data from the server
- * 4.e. unbind the agent [and kill the app?]
- * 5. shut down the transport
+ * 3. for each app in the restore queue:
+ * 3.a. clear the app data
+ * 3.b. get the restore data for the app from the transport
+ * 3.c. launch the backup agent for the app
+ * 3.d. agent.doRestore() with the data from the server
+ * 3.e. unbind the agent [and kill the app?]
+ * 4. shut down the transport
*/
- int err = -1;
+ int error = -1; // assume error
+
+ // build the set of apps to restore
try {
- err = mTransport.startSession();
- } catch (Exception e) {
- Log.e(TAG, "Error starting transport for restore");
- e.printStackTrace();
- }
+ RestoreSet[] images = mTransport.getAvailableRestoreSets();
+ if (images == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting restore sets");
+ return;
+ }
- if (err == 0) {
- // build the set of apps to restore
- try {
- RestoreSet[] images = mTransport.getAvailableRestoreSets();
- if (images.length > 0) {
- // !!! TODO: pick out the set for this token
- mImage = images[0];
+ if (images.length == 0) {
+ Log.i(TAG, "No restore sets available");
+ return;
+ }
- // Pull the Package Manager metadata from the restore set first
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentApps());
- PackageInfo pmApp = new PackageInfo();
- pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
- // !!! TODO: version currently ignored when 'restoring' the PM metadata
- processOneRestore(pmApp, 0,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+ mImage = images[0];
- // build the set of apps we will attempt to restore
- PackageInfo[] packages = mTransport.getAppSet(mImage.token);
- HashSet<RestoreRequest> appsToRestore = new HashSet<RestoreRequest>();
- for (PackageInfo pkg: packages) {
- // get the real PackageManager idea of the package
- PackageInfo app = isRestorable(pkg);
- if (app != null) {
- // Validate against the backed-up signature block, too
- Metadata info = pmAgent.getRestoredMetadata(app.packageName);
- if (info != null) {
- if (app.versionCode >= info.versionCode) {
- if (DEBUG) Log.v(TAG, "Restore version "
- + info.versionCode
- + " compatible with app version "
- + app.versionCode);
- if (signaturesMatch(info.signatures, app.signatures)) {
- appsToRestore.add(
- new RestoreRequest(app, info.versionCode));
- } else {
- Log.w(TAG, "Sig mismatch restoring "
- + app.packageName);
- }
- } else {
- Log.i(TAG, "Restore set for " + app.packageName
- + " is too new [" + info.versionCode
- + "] for installed app version "
- + app.versionCode);
- }
- } else {
- Log.d(TAG, "Unable to get metadata for "
- + app.packageName);
- }
- }
+ // Get the list of all packages which have backup enabled.
+ // (Include the Package Manager metadata pseudo-package first.)
+ ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
+ PackageInfo omPackage = new PackageInfo();
+ omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ restorePackages.add(omPackage);
+
+ List<PackageInfo> agentPackages = allAgentPackages();
+ restorePackages.addAll(agentPackages);
+
+ // let the observer know that we're running
+ if (mObserver != null) {
+ try {
+ // !!! TODO: get an actual count from the transport after
+ // its startRestore() runs?
+ mObserver.restoreStarting(restorePackages.size());
+ } catch (RemoteException e) {
+ Log.d(TAG, "Restore observer died at restoreStarting");
+ mObserver = null;
+ }
+ }
+
+ // STOPSHIP TODO: pick out the set for this token (instead of images[0])
+ long token = images[0].token;
+ if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error starting restore operation");
+ return;
+ }
+
+ String packageName = mTransport.nextRestorePackage();
+ if (packageName == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting first restore package");
+ return;
+ } else if (packageName.equals("")) {
+ Log.i(TAG, "No restore data available");
+ return;
+ } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+ Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+ + "\", found only \"" + packageName + "\"");
+ return;
+ }
+
+ // Pull the Package Manager metadata from the restore set first
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, agentPackages);
+ processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
+ int count = 0;
+ for (;;) {
+ packageName = mTransport.nextRestorePackage();
+ if (packageName == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting next restore package");
+ return;
+ } else if (packageName.equals("")) {
+ break;
+ }
+
+ if (mObserver != null) {
+ ++count;
+ try {
+ mObserver.onUpdate(count);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Restore observer died in onUpdate");
+ mObserver = null;
}
-
- // now run the restore queue
- doQueuedRestores(appsToRestore);
}
- } catch (RemoteException e) {
- // can't happen; transports run locally
- }
- // done; shut down the transport
- try {
- mTransport.endSession();
- } catch (Exception e) {
- Log.e(TAG, "Error ending transport for restore");
- e.printStackTrace();
- }
- }
+ Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
+ if (metaInfo == null) {
+ Log.e(TAG, "Missing metadata for " + packageName);
+ continue;
+ }
- // even if the initial session startup failed, report that we're done here
- }
+ int flags = PackageManager.GET_SIGNATURES;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ if (metaInfo.versionCode > packageInfo.versionCode) {
+ Log.w(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is too new for installed version ["
+ + packageInfo.versionCode + "]");
+ continue;
+ }
- // restore each app in the queue
- void doQueuedRestores(HashSet<RestoreRequest> appsToRestore) {
- for (RestoreRequest req : appsToRestore) {
- PackageInfo app = req.app;
- Log.d(TAG, "starting agent for restore of " + app);
+ if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+ Log.w(TAG, "Signature mismatch restoring " + packageName);
+ continue;
+ }
- try {
- // Remove the app's data first
- clearApplicationDataSynchronous(app.packageName);
+ if (DEBUG) Log.v(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is compatible with installed version ["
+ + packageInfo.versionCode + "]");
- // Now perform the restore into the clean app
- IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+ // Now perform the actual restore
+ clearApplicationDataSynchronous(packageName);
+ IBackupAgent agent = bindToAgentSynchronous(
+ packageInfo.applicationInfo,
IApplicationThread.BACKUP_MODE_RESTORE);
- if (agent != null) {
- processOneRestore(app, req.storedAppVersion, agent);
+ if (agent == null) {
+ Log.w(TAG, "Can't find backup agent for " + packageName);
+ continue;
}
- // unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(app.applicationInfo);
- } catch (SecurityException ex) {
- // Try for the next one.
- Log.d(TAG, "error in bind", ex);
- } catch (RemoteException e) {
- // can't happen
+ try {
+ processOneRestore(packageInfo, metaInfo.versionCode, agent);
+ } finally {
+ // unbind even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
+ }
}
+ // if we get this far, report success to the observer
+ error = 0;
+ } catch (NameNotFoundException e) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Invalid paackage restoring data", e);
+ } catch (RemoteException e) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error restoring data", e);
+ } finally {
+ try {
+ mTransport.finishRestore();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finishing restore", e);
+ }
+
+ if (mObserver != null) {
+ try {
+ mObserver.restoreFinished(error);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Restore observer died at restoreFinished");
+ }
+ }
}
}
- // Do the guts of a restore of one application, derived from the 'mImage'
- // restore set via the 'mTransport' transport.
- void processOneRestore(PackageInfo app, int storedAppVersion, IBackupAgent agent) {
+ // Do the guts of a restore of one application, using mTransport.getRestoreData().
+ void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
// !!! TODO: actually run the restore through mTransport
final String packageName = app.packageName;
@@ -954,11 +980,12 @@
// Run the transport's restore pass
// Run the target's backup pass
- int err = -1;
try {
- err = mTransport.getRestoreData(mImage.token, app, backupData);
- } catch (RemoteException e) {
- // can't happen
+ if (!mTransport.getRestoreData(backupData)) {
+ // STOPSHIP TODO: Handle this error somehow?
+ Log.e(TAG, "Error getting restore data for " + packageName);
+ return;
+ }
} finally {
backupData.close();
}
@@ -973,30 +1000,18 @@
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- boolean success = false;
try {
- agent.doRestore(backupData, storedAppVersion, newState);
- success = true;
- } catch (Exception e) {
- Log.e(TAG, "Restore failed for " + packageName);
- e.printStackTrace();
+ agent.doRestore(backupData, appVersionCode, newState);
} finally {
newState.close();
backupData.close();
}
// if everything went okay, remember the recorded state now
- if (success) {
- File savedStateName = new File(mStateDir, packageName);
- newStateName.renameTo(savedStateName);
- }
- } catch (FileNotFoundException fnfe) {
- Log.v(TAG, "Couldn't open file for restore: " + fnfe);
- } catch (IOException ioe) {
- Log.e(TAG, "Unable to process restore file: " + ioe);
+ File savedStateName = new File(mStateDir, packageName);
+ newStateName.renameTo(savedStateName);
} catch (Exception e) {
- Log.e(TAG, "Final exception guard in restore:");
- e.printStackTrace();
+ Log.e(TAG, "Error restoring data for " + packageName, e);
}
}
}
@@ -1183,14 +1198,15 @@
}
}
- public int performRestore(int token) throws android.os.RemoteException {
+ public int performRestore(int token, IRestoreObserver observer)
+ throws android.os.RemoteException {
mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
if (mRestoreSets != null) {
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
- mRestoreTransport);
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer);
msg.arg1 = token;
mBackupHandler.sendMessage(msg);
return 0;
@@ -1206,7 +1222,7 @@
mContext.enforceCallingPermission("android.permission.BACKUP",
"endRestoreSession");
- mRestoreTransport.endSession();
+ mRestoreTransport.finishRestore();
mRestoreTransport = null;
synchronized(BackupManagerService.this) {
if (BackupManagerService.this.mActiveRestoreSession == this) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index d620eb1..66fb86d 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -57,7 +57,7 @@
// is stored using the package name as a key)
private static final String GLOBAL_METADATA_KEY = "@meta@";
- private List<ApplicationInfo> mAllApps;
+ private List<PackageInfo> mAllPackages;
private PackageManager mPackageManager;
private HashMap<String, Metadata> mRestoredSignatures;
@@ -73,9 +73,9 @@
// We're constructed with the set of applications that are participating
// in backup. This set changes as apps are installed & removed.
- PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
+ PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
mPackageManager = packageMgr;
- mAllApps = apps;
+ mAllPackages = packages;
mRestoredSignatures = null;
}
@@ -118,8 +118,8 @@
// For each app we have on device, see if we've backed it up yet. If not,
// write its signature block to the output, keyed on the package name.
- for (ApplicationInfo app : mAllApps) {
- String packName = app.packageName;
+ for (PackageInfo pkg : mAllPackages) {
+ String packName = pkg.packageName;
if (!existing.contains(packName)) {
// We haven't stored this app's signatures yet, so we do that now
try {
@@ -186,7 +186,7 @@
}
// Finally, write the new state blob -- just the list of all apps we handled
- writeStateFile(mAllApps, newState);
+ writeStateFile(mAllPackages, newState);
}
// "Restore" here is a misnomer. What we're really doing is reading back the
@@ -327,7 +327,7 @@
}
// Util: write out our new backup state file
- private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
+ private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
@@ -338,8 +338,8 @@
out.write(metaNameBuf);
// now write all the app names too
- for (ApplicationInfo app : apps) {
- byte[] pkgNameBuf = app.packageName.getBytes();
+ for (PackageInfo pkg : pkgs) {
+ byte[] pkgNameBuf = pkg.packageName.getBytes();
out.writeInt(pkgNameBuf.length);
out.write(pkgNameBuf);
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index d2e5555..857bfaa 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -968,35 +968,7 @@
if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLP(a.info, flags)) {
- ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags);
- if (ainfo != null) {
- ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(),
- PackageManager.GET_SUPPORTS_DENSITIES);
- if (appInfo != null &&
- (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) {
- // Check if the screen size is same as what the application expect.
- CompatibilityInfo info = new CompatibilityInfo(appInfo);
- DisplayMetrics metrics = new DisplayMetrics();
- metrics.setTo(mMetrics);
- int orientation = mMetrics.widthPixels > mMetrics.heightPixels ?
- Configuration.ORIENTATION_LANDSCAPE :
- Configuration.ORIENTATION_PORTRAIT;
- metrics.updateMetrics(info, orientation);
- if (!info.mExpandable) {
- // Don't allow an app that cannot expand to handle rotation.
- ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION;
- } else {
- appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
- }
- if (DEBUG_SETTINGS) {
- Log.d(TAG, "component=" + component +
- ", expandable:" +
- ((appInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0));
- }
- }
- }
- return ainfo;
+ return PackageParser.generateActivityInfo(a, flags);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java
index 5532894..d921baf 100644
--- a/services/java/com/android/server/WallpaperService.java
+++ b/services/java/com/android/server/WallpaperService.java
@@ -18,8 +18,10 @@
import static android.os.FileObserver.*;
import static android.os.ParcelFileDescriptor.*;
+
import android.app.IWallpaperService;
import android.app.IWallpaperServiceCallback;
+import android.backup.BackupManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -154,7 +156,16 @@
public ParcelFileDescriptor setWallpaper() {
checkPermission(android.Manifest.permission.SET_WALLPAPER);
try {
- return ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+ MODE_CREATE|MODE_READ_WRITE);
+
+ // changing the wallpaper means we'll need to back up the new one
+ long origId = Binder.clearCallingIdentity();
+ BackupManager bm = new BackupManager(mContext);
+ bm.dataChanged();
+ Binder.restoreCallingIdentity(origId);
+
+ return fd;
} catch (FileNotFoundException e) {
if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 969fbfe..e91798b 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1349,39 +1349,42 @@
level = 0;
}
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
+ flags = result[3];
+ ssid = "";
+ } else {
+ flags = "";
+ ssid = result[3];
+ }
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+
// bssid is the hash key
scanResult = mScanResultCache.get(bssid);
if (scanResult != null) {
scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
} else {
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
- flags = result[3];
- ssid = result[4];
- } else {
- // Here, we must have 3 fields: no flags and ssid
- // set
- flags = "";
- ssid = "";
- }
-
// Do not add scan results that have no SSID set
if (0 < ssid.trim().length()) {
scanResult =
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5236a91..8cdc1dc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1524,8 +1524,7 @@
}
}
- final BatteryStatsImpl bstats =
- (BatteryStatsImpl) mBatteryStatsService.getActiveStatistics();
+ final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
synchronized(mPidsSelfLocked) {
if (haveNewCpuStats) {
@@ -1540,7 +1539,7 @@
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
} else {
BatteryStatsImpl.Uid.Proc ps =
- bstats.getProcessStatsLocked(st.name);
+ bstats.getProcessStatsLocked(st.name, st.pid);
if (ps != null) {
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a79eb3a..bf5df88 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -612,6 +612,21 @@
}
/**
+ * Returns the voice mail count.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * @hide
+ */
+ public int getVoiceMessageCount() {
+ try {
+ return getITelephony().getVoiceMessageCount();
+ } catch (RemoteException ex) {
+ }
+ return 0;
+ }
+
+ /**
* Retrieves the alphabetic identifier associated with the voice
* mail number.
* <p>
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6e6f64c..d83b135 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -241,7 +241,7 @@
/**
* Returns the unread count of voicemails
*/
- int getCountVoiceMessages();
+ int getVoiceMessageCount();
}
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index 114094b..ea24c25 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -196,7 +196,7 @@
* If not available (eg, on an older CPHS SIM) -1 is returned if
* getVoiceMessageWaiting() is true
*/
- public int getCountVoiceMessages() {
+ public int getVoiceMessageCount() {
return countVoiceMessages;
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index cdbfd61..b02c692 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -119,8 +119,11 @@
static final String APN_TYPE_MMS = "mms";
/** APN type for SUPL assisted GPS */
static final String APN_TYPE_SUPL = "supl";
+ /** APN type for default data traffic, when requested using startUsingNetworkFeature */
+ static final String APN_TYPE_DEFAULT_FEATURE = "default-feature";
// "Features" accessible through the connectivity manager
+ static final String FEATURE_ENABLE_DEFAULT = "enableDEFAULT";
static final String FEATURE_ENABLE_MMS = "enableMMS";
static final String FEATURE_ENABLE_SUPL = "enableSUPL";
@@ -831,7 +834,7 @@
* Returns unread voicemail count. This count is shown when the voicemail
* notification is expanded.<p>
*/
- int getCountVoiceMessages();
+ int getVoiceMessageCount();
/**
* Returns the alpha tag associated with the voice mail number.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index c34f26e..f6665aa 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -651,6 +651,11 @@
mNotifier.notifyDataActivity(this);
}
+ public void notifyMessageWaitingIndicator() {
+ // This function is added to send the notification to DefaultPhoneNotifier.
+ mNotifier.notifyMessageWaitingChanged(this);
+ }
+
public void notifyDataConnection(String reason) {
mNotifier.notifyDataConnection(this, reason);
}
@@ -658,7 +663,7 @@
public abstract String getPhoneName();
/** @hide */
- public int getCountVoiceMessages(){
+ public int getVoiceMessageCount(){
return 0;
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 4bb24dc..5b3c8dd 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -435,8 +435,8 @@
}
/** @hide */
- public int getCountVoiceMessages(){
- return mActivePhone.getCountVoiceMessages();
+ public int getVoiceMessageCount(){
+ return mActivePhone.getVoiceMessageCount();
}
public String getVoiceMailAlphaTag() {
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 9f780c9..4db3e5b 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1365,7 +1365,6 @@
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result);
- rr.mp.writeInt(2);
rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass
// cause code according to X.S004-550E
rr.mp.writeInt(cause);
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index a3016fa..890ea63 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -78,6 +78,7 @@
protected static final String[] RAW_PROJECTION = new String[] {
"pdu",
"sequence",
+ "destination_port",
};
static final int MAIL_SEND_SMS = 1;
@@ -289,7 +290,13 @@
sms = (SmsMessage) ar.result;
try {
if (mStorageAvailable) {
- dispatchMessage(sms.mWrappedSmsMessage);
+ int result = dispatchMessage(sms.mWrappedSmsMessage);
+ if (result != Activity.RESULT_OK) {
+ // RESULT_OK means that message was broadcast for app(s) to handle.
+ // Any other result, we should ack here.
+ boolean handled = (result == Intents.RESULT_SMS_HANDLED);
+ acknowledgeLastIncomingSms(handled, result, null);
+ }
} else {
acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_OUT_OF_MEMORY, null);
}
@@ -469,8 +476,11 @@
* Dispatches an incoming SMS messages.
*
* @param sms the incoming message from the phone
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- protected abstract void dispatchMessage(SmsMessageBase sms);
+ protected abstract int dispatchMessage(SmsMessageBase sms);
/**
@@ -478,8 +488,11 @@
* the part is stored for later processing.
*
* NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- protected void processMessagePart(SmsMessageBase sms,
+ protected int processMessagePart(SmsMessageBase sms,
SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
// Lookup all other related parts
@@ -506,8 +519,7 @@
values.put("destination_port", portAddrs.destPort);
}
mResolver.insert(mRawUri, values);
- acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
// All the parts are in place, deal with them
@@ -529,8 +541,7 @@
} catch (SQLException e) {
Log.e(TAG, "Can't access multipart SMS database", e);
// TODO: Would OUT_OF_MEMORY be more appropriate?
- acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
} finally {
if (cursor != null) cursor.close();
}
@@ -555,7 +566,7 @@
output.write(data, 0, data.length);
}
// Handle the PUSH
- mWapPush.dispatchWapPdu(output.toByteArray());
+ return mWapPush.dispatchWapPdu(output.toByteArray());
} else {
// The messages were sent to a port, so concoct a URI for it
dispatchPortAddressedPdus(pdus, portAddrs.destPort);
@@ -564,6 +575,7 @@
// The messages were not sent to a port
dispatchPdus(pdus);
}
+ return Activity.RESULT_OK;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index a9aacda..99709406 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -16,8 +16,10 @@
package com.android.internal.telephony;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.util.Config;
import android.util.Log;
@@ -51,8 +53,11 @@
* wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
*
* @param pdu The WAP PDU, made up of one or more SMS PDUs
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- public void dispatchWapPdu(byte[] pdu) {
+ public int dispatchWapPdu(byte[] pdu) {
if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
@@ -64,7 +69,7 @@
if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
pduDecoder = new WspTypeDecoder(pdu);
@@ -77,7 +82,7 @@
*/
if (pduDecoder.decodeUintvarInteger(index) == false) {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error.");
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
headerLength = (int)pduDecoder.getValue32();
index += pduDecoder.getDecodedDataLength();
@@ -98,7 +103,7 @@
*/
if (pduDecoder.decodeContentType(index) == false) {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
int binaryContentType;
String mimeType = pduDecoder.getValueString();
@@ -128,7 +133,7 @@
Log.w(LOG_TAG,
"Received PDU. Unsupported Content-Type = " + binaryContentType);
}
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
} else {
if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) {
@@ -145,7 +150,7 @@
binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
} else {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
}
index += pduDecoder.getDecodedDataLength();
@@ -167,6 +172,7 @@
if (dispatchedByApplication == false) {
dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, dataIndex);
}
+ return Activity.RESULT_OK;
}
private void dispatchWapPdu_default(
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 9aa7eab..3362de8 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -19,6 +19,7 @@
import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -26,6 +27,7 @@
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.SystemProperties;
+import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
@@ -40,6 +42,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DataConnection;
import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccException;
import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.IccPhoneBookInterfaceManager;
import com.android.internal.telephony.IccSmsInterfaceManager;
@@ -65,6 +68,9 @@
// Default Emergency Callback Mode exit timer
private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 30000;
+ static final String VM_COUNT_CDMA = "vm_count_key_cdma";
+ private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
+ private String mVmNumber = null;
//***** Instance Variables
CdmaCallTracker mCT;
@@ -147,6 +153,9 @@
// This is needed to handle phone process crashes
String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
mIsPhoneInECMState = inEcm.equals("true");
+
+ // Notify voicemails.
+ notifier.notifyMessageWaitingChanged(this);
}
public void dispose() {
@@ -300,7 +309,7 @@
public boolean
getMessageWaitingIndicator() {
- return mRuimRecords.getVoiceMessageWaiting();
+ return (getVoiceMessageCount() > 0);
}
public List<? extends MmiCode>
@@ -678,22 +687,33 @@
public void setVoiceMailNumber(String alphaTag,
String voiceMailNumber,
Message onComplete) {
- //mSIMRecords.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
- //TODO: Where do we have to store this value has to be clarified with QC
+ Message resp;
+ mVmNumber = voiceMailNumber;
+ resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+ mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
public String getVoiceMailNumber() {
- //TODO: Where can we get this value has to be clarified with QC
- //return mSIMRecords.getVoiceMailNumber();
-// throw new RuntimeException();
- return "*86";
+ String number = null;
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ // TODO(Moto): The default value of voicemail number should be read from a system property
+ number = sp.getString(VM_NUMBER_CDMA, "*86");
+ return number;
}
/* Returns Number of Voicemails
* @hide
*/
- public int getCountVoiceMessages() {
- return mRuimRecords.getCountVoiceMessages();
+ public int getVoiceMessageCount() {
+ int voicemailCount = mRuimRecords.getVoiceMessageCount();
+ // If mRuimRecords.getVoiceMessageCount returns zero, then there is possibility
+ // that phone was power cycled and would have lost the voicemail count.
+ // So get the count from preferences.
+ if (voicemailCount == 0) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ voicemailCount = sp.getInt(VM_COUNT_CDMA, 0);
+ }
+ return voicemailCount;
}
public String getVoiceMailAlphaTag() {
@@ -820,9 +840,10 @@
mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
}
- public void
- notifyMessageWaitingIndicator() {
- mNotifier.notifyMessageWaitingChanged(this);
+ /* This function is overloaded to send number of voicemails instead of sending true/false */
+ /*package*/ void
+ updateMessageWaitingIndicator(int mwi) {
+ mRuimRecords.setVoiceMessageWaiting(1, mwi);
}
/**
@@ -984,6 +1005,19 @@
}
break;
+ case EVENT_SET_VM_NUMBER_DONE:{
+ ar = (AsyncResult)msg.obj;
+ if (IccException.class.isInstance(ar.exception)) {
+ storeVoiceMailNumber(mVmNumber);
+ ar.exception = null;
+ }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
+ }
+ }
+
default:{
throw new RuntimeException("unexpected event not handled");
}
@@ -1198,4 +1232,16 @@
int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
return mEriManager.getCdmaEriText(roamInd, defRoamInd);
}
+
+ /**
+ * Store the voicemail number in preferences
+ */
+ private void storeVoiceMailNumber(String number) {
+ // Update the preference value of voicemail number
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(VM_NUMBER_CDMA, number);
+ editor.commit();
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index da9fd0c4..ed2ea90 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -253,11 +253,7 @@
// Always unmute when answering a new call
setMute(false);
cm.acceptCall(obtainCompleteMessage());
- } else if ((foregroundCall.connections.size() > 0) &&
- (ringingCall.getState() == CdmaCall.State.WAITING)) {
- // TODO(Moto): jsh asks, "Is this check necessary? I don't think it should be
- // possible to have no fg connection and a WAITING call, but if we should hit
- // this situation, is a CallStateExcetion appropriate?"
+ } else if (ringingCall.getState() == CdmaCall.State.WAITING) {
CdmaConnection cwConn = (CdmaConnection)(ringingCall.getLatestConnection());
// Since there is no network response for supplimentary
@@ -530,10 +526,6 @@
CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n);
droppedDuringPoll.add(cn);
}
- // TODO(Moto): jsh asks, "Are we sure we don't need to do this for
- // ringingCall as well? What if there's a WAITING call (ie, UI timer
- // hasn't expired, moving it to DISCONNECTED)? How/when will it
- // transition to DISCONNECTED?"
}
foregroundCall.setGeneric(false);
// Dropped connections are removed from the CallTracker
@@ -681,8 +673,12 @@
// set the ringing call state to IDLE here to avoid a race condition
// where a new call waiting could get a hang up from an old call
// waiting ringingCall.
- // TODO(Moto): jsh asks, "Should we call conn.ondisconnect() here or Somewhere?"
- ringingCall.detach(conn);
+ //
+ // PhoneApp does the call log itself since only PhoneApp knows
+ // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
+ // is not called here. Instead, conn.onLocalDisconnect() is called.
+ conn.onLocalDisconnect();
+ phone.notifyCallStateChanged();
return;
} else {
try {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 588bdf0..025382d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -452,12 +452,7 @@
this.cause = cause;
if (!disconnected) {
- index = -1;
-
- disconnectTime = System.currentTimeMillis();
- duration = SystemClock.elapsedRealtime() - connectTimeReal;
- disconnected = true;
-
+ doDisconnect();
if (Config.LOGD) Log.d(LOG_TAG,
"[CDMAConn] onDisconnect: cause=" + cause);
@@ -470,6 +465,21 @@
releaseWakeLock();
}
+ /** Called when the call waiting connection has been hung up */
+ /*package*/ void
+ onLocalDisconnect() {
+ if (!disconnected) {
+ doDisconnect();
+ if (Config.LOGD) Log.d(LOG_TAG,
+ "[CDMAConn] onLoalDisconnect" );
+
+ if (parent != null) {
+ parent.detach(this);
+ }
+ }
+ releaseWakeLock();
+ }
+
// Returns true if state has changed, false if nothing changed
/*package*/ boolean
update (DriverCall dc) {
@@ -587,6 +597,14 @@
}
private void
+ doDisconnect() {
+ index = -1;
+ disconnectTime = System.currentTimeMillis();
+ duration = SystemClock.elapsedRealtime() - connectTimeReal;
+ disconnected = true;
+ }
+
+ private void
onStartedHolding() {
holdingStartTime = SystemClock.elapsedRealtime();
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 2b4a700..ecdc8f6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -20,11 +20,14 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ContentValues;
+import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.SQLException;
import android.os.AsyncResult;
import android.os.Message;
+import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
+import android.preference.PreferenceManager;
import android.util.Config;
import android.util.Log;
@@ -64,17 +67,12 @@
Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!");
}
- /**
- * Dispatches an incoming SMS messages.
- *
- * @param smsb the incoming message from the phone
- */
- protected void dispatchMessage(SmsMessageBase smsb) {
+ /** {@inheritDoc} */
+ protected int dispatchMessage(SmsMessageBase smsb) {
// If sms is null, means there was a parsing error.
- // TODO: Should NAK this.
if (smsb == null) {
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
// Decode BD stream and set sms variables.
@@ -83,27 +81,6 @@
int teleService = sms.getTeleService();
boolean handled = false;
- // Teleservices W(E)MT and VMN are handled together:
- if ((teleService == SmsEnvelope.TELESERVICE_WMT)
- || (teleService == SmsEnvelope.TELESERVICE_WEMT)
- || (teleService == SmsEnvelope.TELESERVICE_VMN)) {
- // From here on we need decoded BD.
- // Special case the message waiting indicator messages
- if (sms.isMWISetMessage()) {
- mCdmaPhone.updateMessageWaitingIndicator(true);
- handled |= sms.isMwiDontStore();
- if (Config.LOGD) {
- Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
- }
- } else if (sms.isMWIClearMessage()) {
- mCdmaPhone.updateMessageWaitingIndicator(false);
- handled |= sms.isMwiDontStore();
- if (Config.LOGD) {
- Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
- }
- }
- }
-
if (sms.getUserData() == null) {
if (Config.LOGD) {
Log.d(TAG, "Received SMS without user data");
@@ -111,11 +88,25 @@
handled = true;
}
- if (handled) return;
+ if (handled) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
if (SmsEnvelope.TELESERVICE_WAP == teleService){
- processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress());
- return;
+ return processCdmaWapPdu(sms.getUserData(), sms.messageRef,
+ sms.getOriginatingAddress());
+ } else if (SmsEnvelope.TELESERVICE_VMN == teleService) {
+ // handling Voicemail
+ int voicemailCount = sms.getNumOfVoicemails();
+ Log.d(TAG, "Voicemail count=" + voicemailCount);
+ // Store the voicemail count in preferences.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ ((CDMAPhone) mPhone).getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
+ editor.commit();
+ ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
+ return Intents.RESULT_SMS_HANDLED;
}
/**
@@ -145,17 +136,19 @@
if (smsHeader != null && smsHeader.portAddrs != null) {
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
// GSM-style WAP indication
- mWapPush.dispatchWapPdu(sms.getUserData());
+ return mWapPush.dispatchWapPdu(sms.getUserData());
+ } else {
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
}
- // The message was sent to a port, so concoct a URI for it.
- dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
} else {
// Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
+ return Activity.RESULT_OK;
} else {
// Process the message part.
- processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
+ return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
}
}
@@ -165,29 +158,35 @@
* WDP segments are gathered until a datagram completes and gets dispatched.
*
* @param pdu The WAP-WDP PDU segment
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- protected void processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
+ protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
int segment;
int totalSegments;
int index = 0;
int msgType;
- int sourcePort;
- int destinationPort;
+ int sourcePort = 0;
+ int destinationPort = 0;
msgType = pdu[index++];
if (msgType != 0){
Log.w(TAG, "Received a WAP SMS which is not WDP. Discard.");
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
totalSegments = pdu[index++]; // >=1
segment = pdu[index++]; // >=0
- //process WDP segment
- sourcePort = (0xFF & pdu[index++]) << 8;
- sourcePort |= 0xFF & pdu[index++];
- destinationPort = (0xFF & pdu[index++]) << 8;
- destinationPort |= 0xFF & pdu[index++];
+ // Only the first segment contains sourcePort and destination Port
+ if (segment == 0) {
+ //process WDP segment
+ sourcePort = (0xFF & pdu[index++]) << 8;
+ sourcePort |= 0xFF & pdu[index++];
+ destinationPort = (0xFF & pdu[index++]) << 8;
+ destinationPort |= 0xFF & pdu[index++];
+ }
// Lookup all other related parts
StringBuilder where = new StringBuilder("reference_number =");
@@ -217,7 +216,7 @@
mResolver.insert(mRawUri, values);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
// All the parts are in place, deal with them
@@ -228,6 +227,11 @@
for (int i = 0; i < cursorCount; i++) {
cursor.moveToNext();
int cursorSequence = (int)cursor.getLong(sequenceColumn);
+ // Read the destination port from the first segment
+ if (cursorSequence == 0) {
+ int destinationPortColumn = cursor.getColumnIndex("destination_port");
+ destinationPort = (int)cursor.getLong(destinationPortColumn);
+ }
pdus[cursorSequence] = HexDump.hexStringToByteArray(
cursor.getString(pduColumn));
}
@@ -237,7 +241,7 @@
mResolver.delete(mRawUri, where.toString(), whereArgs);
} catch (SQLException e) {
Log.e(TAG, "Can't access multipart SMS database", e);
- return; // TODO: NACK the message or something, don't just discard.
+ return Intents.RESULT_SMS_GENERIC_ERROR;
} finally {
if (cursor != null) cursor.close();
}
@@ -257,15 +261,14 @@
switch (destinationPort) {
case SmsHeader.PORT_WAP_PUSH:
// Handle the PUSH
- mWapPush.dispatchWapPdu(datagram);
- break;
+ return mWapPush.dispatchWapPdu(datagram);
default:{
pdus = new byte[1][];
pdus[0] = datagram;
// The messages were sent to any other WAP port
dispatchPortAddressedPdus(pdus, destinationPort);
- break;
+ return Activity.RESULT_OK;
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 63d2c47..3a92064 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -773,6 +773,12 @@
return asciiDigit;
}
+ /** This function shall be called to get the number of voicemails.
+ * @hide
+ */
+ /*package*/ int getNumOfVoicemails() {
+ return mBearerData.numberOfMessages;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 70d71fc..d1e4b4f 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -448,11 +448,6 @@
}
public void
- notifyMessageWaitingIndicator() {
- mNotifier.notifyMessageWaitingChanged(this);
- }
-
- public void
notifyCallForwardingIndicator() {
mNotifier.notifyCallForwardingChanged(this);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 035c690..25a5f51 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -130,7 +130,8 @@
private static int APN_DEFAULT_ID = 0;
private static int APN_MMS_ID = 1;
private static int APN_SUPL_ID = 2;
- private static int APN_NUM_TYPES = 3;
+ private static int APN_DEFAULT_FEATURE_ID = 3;
+ private static int APN_NUM_TYPES = 4;
private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
@@ -317,7 +318,8 @@
/**
* Ensure that we are connected to an APN of the specified type.
* @param type the APN type (currently the only valid values
- * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
+ * are {@link Phone#APN_TYPE_MMS}, {@link Phone#APN_TYPE_SUPL}
+ * and {@link Phone#APN_TYPE_DEFAULT_FEATURE})
* @return the result of the operation. Success is indicated by
* a return value of either {@code Phone.APN_ALREADY_ACTIVE} or
* {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast
@@ -325,7 +327,13 @@
* the APN has been established.
*/
protected int enableApnType(String type) {
- if (!TextUtils.equals(type, Phone.APN_TYPE_MMS) &&
+ /* FIXME: APN_TYPE_DEFAULT_FEATURE is used to request the default APN.
+ * Due to the way mRequestedApnType is used, we needed to add
+ * a different APN_TYPE for this rather than using APN_TYPE_DEFAULT.
+ */
+
+ if (!TextUtils.equals(type, Phone.APN_TYPE_DEFAULT_FEATURE) &&
+ !TextUtils.equals(type, Phone.APN_TYPE_MMS) &&
!TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
return Phone.APN_REQUEST_FAILED;
}
@@ -363,12 +371,14 @@
* use of the default APN has not been explicitly disabled, we are connected
* to the default APN.
* @param type the APN type. The only valid values are currently
- * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
+ * {@link Phone#APN_TYPE_MMS} {@link Phone#APN_TYPE_SUPL} and
+ * {@link Phone#APN_TYPE_DEFAULT_FEATURE}).
* @return
*/
protected int disableApnType(String type) {
Log.d(LOG_TAG, "disableApnType("+type+")");
- if ((TextUtils.equals(type, Phone.APN_TYPE_MMS) ||
+ if ((TextUtils.equals(type, Phone.APN_TYPE_DEFAULT_FEATURE) ||
+ TextUtils.equals(type, Phone.APN_TYPE_MMS) ||
TextUtils.equals(type, Phone.APN_TYPE_SUPL))
&& isEnabled(type)) {
removeMessages(EVENT_RESTORE_DEFAULT_APN);
@@ -419,6 +429,9 @@
private boolean isApnTypeActive(String type) {
// TODO: to support simultaneous, mActiveApn can be a List instead.
+ if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT_FEATURE)) {
+ type = Phone.APN_TYPE_DEFAULT;
+ }
return mActiveApn != null && mActiveApn.canHandleType(type);
}
@@ -440,6 +453,8 @@
return dataEnabled[APN_MMS_ID];
} else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
return dataEnabled[APN_SUPL_ID];
+ } else if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT_FEATURE)) {
+ return dataEnabled[APN_DEFAULT_FEATURE_ID];
} else {
return false;
}
@@ -453,10 +468,13 @@
dataEnabled[APN_MMS_ID] = enable;
} else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
dataEnabled[APN_SUPL_ID] = enable;
+ } else if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT_FEATURE)) {
+ dataEnabled[APN_DEFAULT_FEATURE_ID] = enable;
}
Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] +
" dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID] +
- " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID]);
+ " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID] +
+ " dataEnabled[APN_DEFAULT_FEATURE_ID]=" + dataEnabled[APN_DEFAULT_FEATURE_ID]);
}
/**
@@ -484,7 +502,9 @@
// Don't tear down if there is an active APN and it handles MMS or SUPL.
// TODO: This isn't very general.
if ((isApnTypeActive(Phone.APN_TYPE_MMS) && isEnabled(Phone.APN_TYPE_MMS)) ||
- (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL))) {
+ (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL)) ||
+ (isApnTypeActive(Phone.APN_TYPE_DEFAULT_FEATURE) &&
+ isEnabled(Phone.APN_TYPE_DEFAULT_FEATURE))) {
return false;
}
Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
@@ -514,7 +534,8 @@
* {@code true} otherwise.
*/
public boolean getAnyDataEnabled() {
- return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID];
+ return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] ||
+ dataEnabled[APN_SUPL_ID] || dataEnabled[APN_DEFAULT_FEATURE_ID];
}
/**
@@ -1289,7 +1310,8 @@
* rather an app that inadvertantly fails to reset to the
* default APN, or that dies before doing so.
*/
- if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID]) {
+ if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID] ||
+ dataEnabled[APN_DEFAULT_FEATURE_ID]) {
removeMessages(EVENT_RESTORE_DEFAULT_APN);
sendMessageDelayed(obtainMessage(EVENT_RESTORE_DEFAULT_APN),
getRestoreDefaultApnDelay());
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index f87392a..2770ddc 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -78,24 +78,16 @@
}
}
}
-
- if (mCm != null) {
- mCm.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
- }
+ acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
}
- /**
- * Dispatches an incoming SMS messages.
- *
- * @param sms the incoming message from the phone
- */
- protected void dispatchMessage(SmsMessageBase smsb) {
+ /** {@inheritDoc} */
+ protected int dispatchMessage(SmsMessageBase smsb) {
// If sms is null, means there was a parsing error.
- // TODO: Should NAK this.
if (smsb == null) {
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
SmsMessage sms = (SmsMessage) smsb;
boolean handled = false;
@@ -115,7 +107,9 @@
}
}
- if (handled) return;
+ if (handled) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
SmsHeader smsHeader = sms.getUserDataHeader();
// See if message is partial or port addressed.
@@ -126,17 +120,19 @@
if (smsHeader != null && smsHeader.portAddrs != null) {
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
- mWapPush.dispatchWapPdu(sms.getUserData());
+ return mWapPush.dispatchWapPdu(sms.getUserData());
+ } else {
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
}
- // The message was sent to a port, so concoct a URI for it.
- dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
} else {
// Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
+ return Activity.RESULT_OK;
} else {
// Process the message part.
- processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
+ return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
}
}
diff --git a/test-runner/android/test/RenamingDelegatingContext.java b/test-runner/android/test/RenamingDelegatingContext.java
index 3f64340..d780502 100644
--- a/test-runner/android/test/RenamingDelegatingContext.java
+++ b/test-runner/android/test/RenamingDelegatingContext.java
@@ -136,6 +136,11 @@
return false;
}
}
+
+ @Override
+ public File getDatabasePath(String name) {
+ return mFileContext.getDatabasePath(renamedFileName(name));
+ }
@Override
public String[] databaseList() {
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index c6acc66..6acd90c 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -23,6 +23,8 @@
{
public void onCreate() {
addHelper("data_files", new FileBackupHelper(this, BackupTestActivity.FILE_NAME));
+ addHelper("more_data_files", new FileBackupHelper(this, "another_file.txt", "3.txt",
+ "empty.txt"));
}
}
diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh
new file mode 100755
index 0000000..6ef5dff
--- /dev/null
+++ b/tests/backup/test_backup.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+#adb kill-server
+
+# set the transport
+adb shell bmgr transport 1
+
+# load up the three files
+adb shell "rm /data/data/com.android.backuptest/files/* ; \
+ mkdir /data/data/com.android.backuptest ; \
+ mkdir /data/data/com.android.backuptest/files ; \
+ echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \
+ echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \
+ echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \
+ echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \
+"
+
+# say that the data has changed
+adb shell bmgr backup com.android.backuptest
+
+# run the backup
+adb shell bmgr run
diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh
new file mode 100755
index 0000000..f3d581e
--- /dev/null
+++ b/tests/backup/test_restore.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+function check_file
+{
+ data=$(adb shell cat /data/data/com.android.backuptest/files/$1)
+ if [ "$data" = "$2" ] ; then
+ echo "$1 has correct value [$2]"
+ else
+ echo $1 is INCORRECT
+ echo " value: [$data]"
+ echo " expected: [$2]"
+ fi
+}
+
+# delete the old data
+echo --- Previous files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+adb shell "rm /data/data/com.android.backuptest/files/*"
+echo --- Erased files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo ---
+
+echo
+echo
+echo
+
+# run the restore
+adb shell bmgr restore 0
+
+echo
+echo
+echo
+
+# check the results
+check_file file.txt "first file"
+check_file another_file.txt "asdf"
+check_file 3.txt "3"
+check_file empty.txt ""
+
+echo
+echo
+echo
+echo --- Restored files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo ---
+echo
diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp
index a7f301e..ae11231 100644
--- a/tools/localize/Perforce.cpp
+++ b/tools/localize/Perforce.cpp
@@ -1,6 +1,7 @@
#include "Perforce.h"
#include "log.h"
#include <string.h>
+#include <cstdio>
#include <stdlib.h>
#include <sstream>
#include <sys/types.h>
diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp
index dd54f3a..184bfe0a 100644
--- a/tools/localize/SourcePos.cpp
+++ b/tools/localize/SourcePos.cpp
@@ -1,6 +1,7 @@
#include "SourcePos.h"
#include <stdarg.h>
+#include <cstdio>
#include <set>
#include <cstdio>
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
index 84081f5..775ce2f 100644
--- a/tools/localize/file_utils.cpp
+++ b/tools/localize/file_utils.cpp
@@ -1,4 +1,5 @@
#include <string.h>
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "file_utils.h"
diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp
index 931ea95..1d0ac9a 100644
--- a/tools/localize/localize_test.cpp
+++ b/tools/localize/localize_test.cpp
@@ -2,6 +2,7 @@
#include "XLIFFFile.h"
#include "ValuesFile.h"
#include "localize.h"
+#include <stdio.h>
int pseudolocalize_xliff(XLIFFFile* xliff, bool expand);
diff --git a/tools/localize/merge_res_and_xliff.cpp b/tools/localize/merge_res_and_xliff.cpp
index 58a6554..1fdaa0e 100644
--- a/tools/localize/merge_res_and_xliff.cpp
+++ b/tools/localize/merge_res_and_xliff.cpp
@@ -3,6 +3,7 @@
#include "file_utils.h"
#include "Perforce.h"
#include "log.h"
+#include <stdio.h>
static set<StringResource>::const_iterator
find_id(const set<StringResource>& s, const string& id, int index)
diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp
index f638a74..6fe2629 100644
--- a/tools/localize/merge_res_and_xliff_test.cpp
+++ b/tools/localize/merge_res_and_xliff_test.cpp
@@ -1,6 +1,6 @@
#include <cstdio>
#include "merge_res_and_xliff.h"
-
+#include <stdio.h>
int
merge_test()
diff --git a/tools/localize/xmb.cpp b/tools/localize/xmb.cpp
index 236705f..d8f6ff0 100644
--- a/tools/localize/xmb.cpp
+++ b/tools/localize/xmb.cpp
@@ -7,6 +7,7 @@
#include "XLIFFFile.h"
#include <map>
+#include <cstdio>
using namespace std;
diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl
index 0e658df..fedccb0 100644
--- a/vpn/java/android/net/vpn/IVpnService.aidl
+++ b/vpn/java/android/net/vpn/IVpnService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
index 181619d..4ae2dec 100644
--- a/vpn/java/android/net/vpn/L2tpIpsecProfile.java
+++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -19,15 +19,14 @@
import android.os.Parcel;
/**
- * The profile for L2TP-over-IPSec type of VPN.
+ * The profile for certificate-based L2TP-over-IPSec type of VPN.
* {@hide}
*/
-public class L2tpIpsecProfile extends VpnProfile {
+public class L2tpIpsecProfile extends L2tpProfile {
private static final long serialVersionUID = 1L;
private String mUserCertificate;
private String mCaCertificate;
- private String mUserkey;
@Override
public VpnType getType() {
@@ -50,20 +49,11 @@
return mUserCertificate;
}
- public void setUserkey(String name) {
- mUserkey = name;
- }
-
- public String getUserkey() {
- return mUserkey;
- }
-
@Override
protected void readFromParcel(Parcel in) {
super.readFromParcel(in);
mCaCertificate = in.readString();
mUserCertificate = in.readString();
- mUserkey = in.readString();
}
@Override
@@ -71,6 +61,5 @@
super.writeToParcel(parcel, flags);
parcel.writeString(mCaCertificate);
parcel.writeString(mUserCertificate);
- parcel.writeString(mUserkey);
}
}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
new file mode 100644
index 0000000..7a03018
--- /dev/null
+++ b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
@@ -0,0 +1,54 @@
+/*
+ * 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.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN.
+ * {@hide}
+ */
+public class L2tpIpsecPskProfile extends L2tpProfile {
+ private static final long serialVersionUID = 1L;
+
+ private String mPresharedKey;
+
+ @Override
+ public VpnType getType() {
+ return VpnType.L2TP_IPSEC_PSK;
+ }
+
+ public void setPresharedKey(String key) {
+ mPresharedKey = key;
+ }
+
+ public String getPresharedKey() {
+ return mPresharedKey;
+ }
+
+ @Override
+ protected void readFromParcel(Parcel in) {
+ super.readFromParcel(in);
+ mPresharedKey = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ super.writeToParcel(parcel, flags);
+ parcel.writeString(mPresharedKey);
+ }
+}
diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java
index 59d4981..dbba0c5 100644
--- a/vpn/java/android/net/vpn/L2tpProfile.java
+++ b/vpn/java/android/net/vpn/L2tpProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -16,6 +16,8 @@
package android.net.vpn;
+import android.os.Parcel;
+
/**
* The profile for L2TP type of VPN.
* {@hide}
@@ -23,8 +25,44 @@
public class L2tpProfile extends VpnProfile {
private static final long serialVersionUID = 1L;
+ private boolean mSecret;
+ private String mSecretString;
+
@Override
public VpnType getType() {
return VpnType.L2TP;
}
+
+ /**
+ * Enables/disables the secret for authenticating tunnel connection.
+ */
+ public void setSecretEnabled(boolean enabled) {
+ mSecret = enabled;
+ }
+
+ public boolean isSecretEnabled() {
+ return mSecret;
+ }
+
+ public void setSecretString(String secret) {
+ mSecretString = secret;
+ }
+
+ public String getSecretString() {
+ return mSecretString;
+ }
+
+ @Override
+ protected void readFromParcel(Parcel in) {
+ super.readFromParcel(in);
+ mSecret = in.readInt() > 0;
+ mSecretString = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ super.writeToParcel(parcel, flags);
+ parcel.writeInt(mSecret ? 1 : 0);
+ parcel.writeString(mSecretString);
+ }
}
diff --git a/vpn/java/android/net/vpn/PptpProfile.java b/vpn/java/android/net/vpn/PptpProfile.java
new file mode 100644
index 0000000..c68bb71
--- /dev/null
+++ b/vpn/java/android/net/vpn/PptpProfile.java
@@ -0,0 +1,30 @@
+/*
+ * 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.net.vpn;
+
+/**
+ * The profile for PPTP type of VPN.
+ * {@hide}
+ */
+public class PptpProfile extends VpnProfile {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public VpnType getType() {
+ return VpnType.PPTP;
+ }
+}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index 98795bd..dc70b26 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl
index ad34bfc..edeaef0 100644
--- a/vpn/java/android/net/vpn/VpnProfile.aidl
+++ b/vpn/java/android/net/vpn/VpnProfile.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java
index 9e24da4..bd6c809 100644
--- a/vpn/java/android/net/vpn/VpnProfile.java
+++ b/vpn/java/android/net/vpn/VpnProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java
index 977d938..ebd9364 100644
--- a/vpn/java/android/net/vpn/VpnState.java
+++ b/vpn/java/android/net/vpn/VpnState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java
index 91b0ea2..c7df943 100644
--- a/vpn/java/android/net/vpn/VpnType.java
+++ b/vpn/java/android/net/vpn/VpnType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -21,14 +21,21 @@
* {@hide}
*/
public enum VpnType {
- L2TP_IPSEC("L2TP/IPSec", L2tpIpsecProfile.class),
- L2TP("L2TP", L2tpProfile.class);
+ PPTP("PPTP", "", PptpProfile.class),
+ L2TP("L2TP", "", L2tpProfile.class),
+ L2TP_IPSEC_PSK("L2TP/IPSec PSK", "Pre-shared key based L2TP/IPSec VPN",
+ L2tpIpsecPskProfile.class),
+ L2TP_IPSEC("L2TP/IPSec CRT", "Certificate based L2TP/IPSec VPN",
+ L2tpIpsecProfile.class);
private String mDisplayName;
+ private String mDescription;
private Class<? extends VpnProfile> mClass;
- VpnType(String displayName, Class<? extends VpnProfile> klass) {
+ VpnType(String displayName, String description,
+ Class<? extends VpnProfile> klass) {
mDisplayName = displayName;
+ mDescription = description;
mClass = klass;
}
@@ -36,6 +43,10 @@
return mDisplayName;
}
+ public String getDescription() {
+ return mDescription;
+ }
+
public Class<? extends VpnProfile> getProfileClass() {
return mClass;
}