Merge "Add body id to ssl_certificate layout"
diff --git a/api/current.txt b/api/current.txt
index f21a4f3..309e747 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3371,6 +3371,7 @@
method public void send(android.content.Context, int, android.content.Intent) throws android.app.PendingIntent.CanceledException;
method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException;
method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException;
+ method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException;
method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -5297,6 +5298,7 @@
method public java.lang.String getTargetPackage();
method public static android.content.IntentSender readIntentSenderOrNullFromParcel(android.os.Parcel);
method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler) throws android.content.IntentSender.SendIntentException;
+ method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler, java.lang.String) throws android.content.IntentSender.SendIntentException;
method public static void writeIntentSenderOrNullToParcel(android.content.IntentSender, android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -22395,6 +22397,7 @@
method public void requestDisallowInterceptTouchEvent(boolean);
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
+ method protected void resetLayoutDirectionResolution();
method public void scheduleLayoutAnimation();
method public void setAddStatesFromChildren(boolean);
method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -26063,6 +26066,7 @@
method protected void onTextChanged(java.lang.CharSequence, int, int, int);
method public boolean onTextContextMenuItem(int);
method public void removeTextChangedListener(android.text.TextWatcher);
+ method protected void resetLayoutDirectionResolution();
method public final void setAutoLinkMask(int);
method public void setCompoundDrawablePadding(int);
method public void setCompoundDrawables(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 164acbc..68c9926 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -31,82 +31,151 @@
* An accessibility service runs in the background and receives callbacks by the system
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
* in the user interface, for example, the focus has changed, a button has been clicked,
- * etc.
+ * etc. Such a service can optionally request the capability for querying the content
+ * of the active window. Development of an accessibility service requires extends this
+ * class and implements its abstract methods.
* <p>
- * An accessibility service extends this class and implements its abstract methods. Such
- * a service is declared as any other service in an AndroidManifest.xml but it must also
- * specify that it handles the "android.accessibilityservice.AccessibilityService"
- * {@link android.content.Intent}. Following is an example of such a declaration:
- * <p>
- * <code>
- * <service android:name=".MyAccessibilityService"><br>
- * <intent-filter><br>
- * <action android:name="android.accessibilityservice.AccessibilityService" /><br>
- * </intent-filter><br>
- * </service><br>
- * </code>
+ * <strong>Lifecycle</strong>
* </p>
* <p>
- * The lifecycle of an accessibility service is managed exclusively by the system. Starting
- * or stopping an accessibility service is triggered by an explicit user action through
+ * The lifecycle of an accessibility service is managed exclusively by the system and
+ * follows the established service life cycle. Additionally, starting or stopping an
+ * accessibility service is triggered exclusively by an explicit user action through
* enabling or disabling it in the device settings. After the system binds to a service it
* calls {@link AccessibilityService#onServiceConnected()}. This method can be
* overriden by clients that want to perform post binding setup.
* </p>
* <p>
+ * <strong>Declaration</strong>
+ * </p>
+ * <p>
+ * An accessibility is declared as any other service in an AndroidManifest.xml but it
+ * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
+ * {@link android.content.Intent}. Failure to declare this intent will cause the system to
+ * ignore the accessibility service. Following is an example declaration:
+ * </p>
+ * <p>
+ * <code>
+ * <pre>
+ * <service android:name=".MyAccessibilityService">
+ * <intent-filter>
+ * <action android:name="android.accessibilityservice.AccessibilityService" />
+ * </intent-filter>
+ * . . .
+ * </service>
+ * </pre>
+ * </code>
+ * </p>
+ * <p>
+ * <strong>Configuration</strong>
+ * </p>
+ * <p>
* An accessibility service can be configured to receive specific types of accessibility events,
* listen only to specific packages, get events from each type only once in a given time frame,
* retrieve window content, specify a settings activity, etc.
* </p>
+ * <p>
* There are two approaches for configuring an accessibility service:
+ * </p>
* <ul>
- * <li>
- * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
- * the service. A service declaration with a meta-data tag is presented below:
- * <p>
- * <code>
- * <service android:name=".MyAccessibilityService"><br>
- * <intent-filter><br>
- * <action android:name="android.accessibilityservice.AccessibilityService" /><br>
- * </intent-filter><br>
- * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br>
- * </service><br>
- * </code>
- * </p>
- * <p>
- * <strong>
- * This approach enables setting all accessibility service properties.
- * </strong>
- * </p>
- * <p>
- * For more details refer to {@link #SERVICE_META_DATA}.
- * </p>
- * </li>
- * <li>
- * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
- * that this method can be called any time to change the service configuration.<br>
- * <p>
- * <strong>
- * This approach enables setting only dynamically configurable accessibility
- * service properties:
- * {@link AccessibilityServiceInfo#eventTypes},
- * {@link AccessibilityServiceInfo#feedbackType},
- * {@link AccessibilityServiceInfo#flags},
- * {@link AccessibilityServiceInfo#notificationTimeout},
- * {@link AccessibilityServiceInfo#packageNames}
- * </strong>
- * </p>
- * <p>
- * For more details refer to {@link AccessibilityServiceInfo}.
- * </p>
- * </li>
+ * <li>
+ * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
+ * the service. A service declaration with a meta-data tag is presented below:
+ * <p>
+ * <code>
+ * <pre>
+ * <service android:name=".MyAccessibilityService">
+ * <intent-filter>
+ * <action android:name="android.accessibilityservice.AccessibilityService" />
+ * </intent-filter>
+ * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" />
+ * </service>
+ * </pre>
+ * </code>
+ * </p>
+ * <p>
+ * <strong>Note:</strong>This approach enables setting all properties.
+ * </p>
+ * <p>
+ * For more details refer to {@link #SERVICE_META_DATA} and
+ * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>..
+ * </p>
+ * </li>
+ * <li>
+ * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
+ * that this method can be called any time to dynamically change the service configuration.
+ * <p>
+ * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
+ * {@link AccessibilityServiceInfo#eventTypes},
+ * {@link AccessibilityServiceInfo#feedbackType},
+ * {@link AccessibilityServiceInfo#flags},
+ * {@link AccessibilityServiceInfo#notificationTimeout},
+ * {@link AccessibilityServiceInfo#packageNames}
+ * </p>
+ * <p>
+ * For more details refer to {@link AccessibilityServiceInfo}.
+ * </p>
+ * </li>
* </ul>
* <p>
- * An accessibility service can be registered for events in specific packages to provide a
- * specific type of feedback and is notified with a certain timeout after the last event
- * of interest has been fired.
+ * <strong>Retrieving window content</strong>
+ * </p>
+ * <p>
+ * An service can specify in its declaration that it can retrieve the active window
+ * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
+ * declaring this capability requires that the service declares its configuration via
+ * an XML resource referenced by {@link #SERVICE_META_DATA}.
+ * </p>
+ * <p>
+ * For security purposes an accessibility service can retrieve only the content of the
+ * currently active window. The currently active window is defined as the window from
+ * which was fired the last event of the following types:
+ * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START},
+ * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END},
+ * {@link AccessibilityEvent#TYPE_VIEW_CLICKED},
+ * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED},
+ * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
+ * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
+ * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED},
+ * {@link AccessibilityEvent#TYPE_VIEW_SELECTED},
+ * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED},
+ * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
+ * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED},
+ * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED},
+ * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}.
+ * In other words, the active window is the one where the user interaction is taking place.
+ * </p>
+ * <p>
+ * The entry point for retrieving window content is through calling
+ * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received
+ * event of the above types or a previous event from the same window
+ * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking
+ * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the
+ * window content which represented as a tree of such objects.
+ * </p>
+ * <p>
+ * <strong>Note</strong>An accessibility service may have requested to be notified for
+ * a subset of the event types, thus be unaware that the active window has changed. Therefore
+ * accessibility service that would like to retrieve window content should:
+ * <ul>
+ * <li>
+ * Register for all event types with no notification timeout and keep track for the active
+ * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and
+ * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval
+ * methods on the latter.
+ * </li>
+ * <li>
+ * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the
+ * active window has changed and the service did not get the accessibility event yet. Note
+ * that it is possible to have a retrieval method failing event adopting the strategy
+ * specified in the previous bullet because the accessibility event dispatch is asynchronous
+ * and crosses process boundaries.
+ * </li>
+ * </ul>
+ * </p>
* <p>
* <b>Notification strategy</b>
+ * </p>
* <p>
* For each feedback type only one accessibility service is notified. Services are notified
* in the order of registration. Hence, if two services are registered for the same
@@ -117,9 +186,10 @@
* registration order. This enables "generic" accessibility services that work reasonably
* well with most applications to coexist with "polished" ones that are targeted for
* specific applications.
+ * </p>
* <p>
* <b>Event types</b>
- * <p>
+ * </p>
* {@link AccessibilityEvent#TYPE_VIEW_CLICKED}
* {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
* {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
@@ -127,9 +197,16 @@
* {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
* {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
* {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
- * <p>
- * <b>Feedback types</b>
- * <p>
+ * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
+ * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
+ * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
+ * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
+ * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
+ * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
+ * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+ * <p>
+ * <b>Feedback types</b>
+ * <p>
* {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
* {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
* {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
@@ -140,10 +217,10 @@
* @see AccessibilityServiceInfo
* @see android.view.accessibility.AccessibilityManager
*
- * Note: The event notification timeout is useful to avoid propagating events to the client
- * too frequently since this is accomplished via an expensive interprocess call.
- * One can think of the timeout as a criteria to determine when event generation has
- * settled down.
+ * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
+ * events to the client too frequently since this is accomplished via an expensive
+ * interprocess call. One can think of the timeout as a criteria to determine when
+ * event generation has settled down.
*/
public abstract class AccessibilityService extends Service {
/**
@@ -154,57 +231,25 @@
/**
* Name under which an AccessibilityService component publishes information
- * about itself. This meta-data must reference an XML resource containing
- * an
+ * about itself. This meta-data must reference an XML resource containing an
* <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>
* tag. This is a a sample XML file configuring an accessibility service:
* <p>
* <code>
- * <?xml version="1.0" encoding="utf-8"?><br>
- * <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br>
- * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br>
- * android:packageNames="foo.bar, foo.baz"<br>
- * android:accessibilityFeedbackType="feedbackSpoken"<br>
- * android:notificationTimeout="100"<br>
- * android:accessibilityFlags="flagDefault"<br>
- * android:settingsActivity="foo.bar.TestBackActivity"<br>
- * . . .<br>
+ * <pre>
+ * <accessibility-service
+ * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
+ * android:packageNames="foo.bar, foo.baz"
+ * android:accessibilityFeedbackType="feedbackSpoken"
+ * android:notificationTimeout="100"
+ * android:accessibilityFlags="flagDefault"
+ * android:settingsActivity="foo.bar.TestBackActivity"
+ * android:canRetrieveWindowContent="true"
+ * . . .
* />
+ * </pre>
* </code>
* </p>
- * <p>
- * <strong>Note:</strong> A service can retrieve only the content of the active window.
- * An active window is the source of the most recent event of type
- * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START},
- * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END},
- * {@link AccessibilityEvent#TYPE_VIEW_CLICKED},
- * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED},
- * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
- * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
- * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED},
- * {@link AccessibilityEvent#TYPE_VIEW_SELECTED},
- * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED},
- * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
- * Therefore the service should:
- * <ul>
- * <li>
- * Register for all event types with no notification timeout and keep track
- * for the active window by calling
- * {@link AccessibilityEvent#getWindowId()} of the last received
- * event and compare this with the
- * {@link AccessibilityNodeInfo#getWindowId()} before calling
- * retrieval methods on the latter.
- * </li>
- * <li>
- * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail
- * since the active window has changed and the service did not get the
- * accessibility event. Note that it is possible to have a retrieval method
- * failing event adopting the strategy specified in the previous bullet
- * because the accessibility event dispatch is asynchronous and crosses
- * process boundaries.
- * </li>
- * <ul>
- * </p>
*/
public static final String SERVICE_META_DATA = "android.accessibilityservice";
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index b9878cd..ef4adca 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -37,13 +37,13 @@
import java.io.IOException;
/**
- * This class describes an {@link AccessibilityService}. The system
- * notifies an {@link AccessibilityService} for
- * {@link android.view.accessibility.AccessibilityEvent}s
+ * This class describes an {@link AccessibilityService}. The system notifies an
+ * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
* according to the information encapsulated in this class.
*
* @see AccessibilityService
* @see android.view.accessibility.AccessibilityEvent
+ * @see android.view.accessibility.AccessibilityManager
*/
public class AccessibilityServiceInfo implements Parcelable {
@@ -93,12 +93,19 @@
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
* @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
* @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
*/
public int eventTypes;
/**
* The package names an {@link AccessibilityService} is interested in. Setting
- * to null is equivalent to all packages.
+ * to <code>null</code> is equivalent to all packages.
* <p>
* <strong>Can be dynamically set at runtime.</strong>
* </p>
@@ -125,10 +132,10 @@
* <strong>Can be dynamically set at runtime.</strong>.
* </p>
* <p>
- * Note: The event notification timeout is useful to avoid propagating events to the client
- * too frequently since this is accomplished via an expensive interprocess call.
- * One can think of the timeout as a criteria to determine when event generation has
- * settled down
+ * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
+ * events to the client too frequently since this is accomplished via an expensive
+ * interprocess call. One can think of the timeout as a criteria to determine when
+ * event generation has settled down.
*/
public long notificationTimeout;
@@ -159,7 +166,7 @@
private String mSettingsActivityName;
/**
- * Flag whether this accessibility service can retrieve screen content.
+ * Flag whether this accessibility service can retrieve window content.
*/
private boolean mCanRetrieveWindowContent;
@@ -296,12 +303,12 @@
}
/**
- * Whether this service can retrieve the currently focused window content.
+ * Whether this service can retrieve the current window's content.
* <p>
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return True screen content is retrieved.
+ * @return True window content can be retrieved.
*/
public boolean getCanRetrieveWindowContent() {
return mCanRetrieveWindowContent;
diff --git a/core/java/android/accessibilityservice/package.html b/core/java/android/accessibilityservice/package.html
new file mode 100644
index 0000000..0c640d1
--- /dev/null
+++ b/core/java/android/accessibilityservice/package.html
@@ -0,0 +1,22 @@
+<html>
+<body>
+<p>
+ The classes in this package are used for development of accessibility service that
+ provide alternative or augmented feedback to the user.
+</p>
+<p>
+ An {@link android.accessibilityservice.AccessibilityService} runs in the background and
+ receives callbacks by the system when {@link android.view.accessibility.AccessibilityEvent}s
+ are fired. Such events denote some state transition in the user interface, for example, the
+ focus has changed, a button has been clicked, etc. Such a service can optionally request the
+ capability for querying the content of the active window. Development of an accessibility
+ service requires extends this class and implements its abstract methods.
+</p>
+<p>
+ An {@link android.accessibilityservice.AccessibilityServiceInfo} describes an
+ {@link android.accessibilityservice.AccessibilityService}. The system notifies an
+ AccessibilityService for {@link android.view.accessibility.AccessibilityEvent}s
+ according to the information encapsulated in this class.
+</p>
+</body>
+</html>
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 85f40c9..fdf4a3a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -251,12 +251,13 @@
IBinder b = data.readStrongBinder();
IApplicationThread app =
b != null ? ApplicationThreadNative.asInterface(b) : null;
+ String packageName = data.readString();
b = data.readStrongBinder();
IIntentReceiver rec
= b != null ? IIntentReceiver.Stub.asInterface(b) : null;
IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
String perm = data.readString();
- Intent intent = registerReceiver(app, rec, filter, perm);
+ Intent intent = registerReceiver(app, packageName, rec, filter, perm);
reply.writeNoException();
if (intent != null) {
reply.writeInt(1);
@@ -1503,6 +1504,16 @@
return true;
}
+ case IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IIntentSender r = IIntentSender.Stub.asInterface(
+ data.readStrongBinder());
+ boolean res = isIntentSenderTargetedToPackage(r);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -1702,7 +1713,7 @@
reply.recycle();
return res;
}
- public Intent registerReceiver(IApplicationThread caller,
+ public Intent registerReceiver(IApplicationThread caller, String packageName,
IIntentReceiver receiver,
IntentFilter filter, String perm) throws RemoteException
{
@@ -1710,6 +1721,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeString(packageName);
data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
filter.writeToParcel(data, 0);
data.writeString(perm);
@@ -3385,5 +3397,18 @@
reply.recycle();
}
+ public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(sender.asBinder());
+ mRemote.transact(IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 94a4afa..8749d3e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -61,7 +61,6 @@
import android.net.wifi.WifiManager;
import android.nfc.NfcManager;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Environment;
@@ -81,7 +80,6 @@
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.view.ContextThemeWrapper;
-import android.view.Display;
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
@@ -142,6 +140,7 @@
new HashMap<String, SharedPreferencesImpl>();
/*package*/ LoadedApk mPackageInfo;
+ private String mBasePackageName;
private Resources mResources;
/*package*/ ActivityThread mMainThread;
private Context mOuterContext;
@@ -1030,7 +1029,7 @@
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
- mMainThread.getApplicationThread(),
+ mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission);
} catch (RemoteException e) {
return null;
@@ -1397,7 +1396,7 @@
if (pi != null) {
ContextImpl c = new ContextImpl();
c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
- c.init(pi, null, mMainThread, mResources);
+ c.init(pi, null, mMainThread, mResources, mBasePackageName);
if (c.mResources != null) {
return c;
}
@@ -1450,6 +1449,7 @@
*/
public ContextImpl(ContextImpl context) {
mPackageInfo = context.mPackageInfo;
+ mBasePackageName = context.mBasePackageName;
mResources = context.mResources;
mMainThread = context.mMainThread;
mContentResolver = context.mContentResolver;
@@ -1458,13 +1458,14 @@
final void init(LoadedApk packageInfo,
IBinder activityToken, ActivityThread mainThread) {
- init(packageInfo, activityToken, mainThread, null);
+ init(packageInfo, activityToken, mainThread, null, null);
}
final void init(LoadedApk packageInfo,
IBinder activityToken, ActivityThread mainThread,
- Resources container) {
+ Resources container, String basePackageName) {
mPackageInfo = packageInfo;
+ mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
mResources = mPackageInfo.getResources(mainThread);
if (mResources != null && container != null
@@ -1485,6 +1486,7 @@
final void init(Resources resources, ActivityThread mainThread) {
mPackageInfo = null;
+ mBasePackageName = null;
mResources = resources;
mMainThread = mainThread;
mContentResolver = new ApplicationContentResolver(this, mainThread);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e2588cf..9e20764 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -103,7 +103,7 @@
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
- public Intent registerReceiver(IApplicationThread caller,
+ public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter,
String requiredPermission) throws RemoteException;
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
@@ -361,6 +361,8 @@
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+ public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -587,4 +589,5 @@
int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+132;
int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133;
+ int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134;
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 5b43b65..b4827cb 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -365,7 +365,7 @@
* is no longer allowing more intents to be sent through it.
*/
public void send() throws CanceledException {
- send(null, 0, null, null, null);
+ send(null, 0, null, null, null, null);
}
/**
@@ -379,7 +379,7 @@
* is no longer allowing more intents to be sent through it.
*/
public void send(int code) throws CanceledException {
- send(null, code, null, null, null);
+ send(null, code, null, null, null, null);
}
/**
@@ -399,7 +399,7 @@
*/
public void send(Context context, int code, Intent intent)
throws CanceledException {
- send(context, code, intent, null, null);
+ send(context, code, intent, null, null, null);
}
/**
@@ -420,7 +420,7 @@
*/
public void send(int code, OnFinished onFinished, Handler handler)
throws CanceledException {
- send(null, code, null, onFinished, handler);
+ send(null, code, null, onFinished, handler, null);
}
/**
@@ -449,20 +449,64 @@
* @see #send(int)
* @see #send(Context, int, Intent)
* @see #send(int, android.app.PendingIntent.OnFinished, Handler)
+ * @see #send(Context, int, Intent, OnFinished, Handler, String)
*
* @throws CanceledException Throws CanceledException if the PendingIntent
* is no longer allowing more intents to be sent through it.
*/
public void send(Context context, int code, Intent intent,
OnFinished onFinished, Handler handler) throws CanceledException {
+ send(context, code, intent, onFinished, handler, null);
+ }
+
+ /**
+ * Perform the operation associated with this PendingIntent, allowing the
+ * caller to specify information about the Intent to use and be notified
+ * when the send has completed.
+ *
+ * <p>For the intent parameter, a PendingIntent
+ * often has restrictions on which fields can be supplied here, based on
+ * how the PendingIntent was retrieved in {@link #getActivity},
+ * {@link #getBroadcast}, or {@link #getService}.
+ *
+ * @param context The Context of the caller. This may be null if
+ * <var>intent</var> is also null.
+ * @param code Result code to supply back to the PendingIntent's target.
+ * @param intent Additional Intent data. See {@link Intent#fillIn
+ * Intent.fillIn()} for information on how this is applied to the
+ * original Intent. Use null to not modify the original Intent.
+ * @param onFinished The object to call back on when the send has
+ * completed, or null for no callback.
+ * @param handler Handler identifying the thread on which the callback
+ * should happen. If null, the callback will happen from the thread
+ * pool of the process.
+ * @param requiredPermission Name of permission that a recipient of the PendingIntent
+ * is required to hold. This is only valid for broadcast intents, and
+ * corresponds to the permission argument in
+ * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+ * If null, no permission is required.
+ *
+ * @see #send()
+ * @see #send(int)
+ * @see #send(Context, int, Intent)
+ * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
+ * @see #send(Context, int, Intent, OnFinished, Handler)
+ *
+ * @throws CanceledException Throws CanceledException if the PendingIntent
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void send(Context context, int code, Intent intent,
+ OnFinished onFinished, Handler handler, String requiredPermission)
+ throws CanceledException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
int res = mTarget.send(code, intent, resolvedType,
onFinished != null
- ? new FinishedDispatcher(this, onFinished, handler)
- : null);
+ ? new FinishedDispatcher(this, onFinished, handler)
+ : null,
+ requiredPermission);
if (res < 0) {
throw new CanceledException();
}
@@ -491,6 +535,20 @@
}
/**
+ * @hide
+ * Check to verify that this PendingIntent targets a specific package.
+ */
+ public boolean isTargetedToPackage() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .isIntentSenderTargetedToPackage(mTarget);
+ } catch (RemoteException e) {
+ // Should never happen.
+ return false;
+ }
+ }
+
+ /**
* Comparison operator on two PendingIntent objects, such that true
* is returned then they both represent the same operation from the
* same package. This allows you to use {@link #getActivity},
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index aecec66..fed6d81 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1029,6 +1029,12 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+ * registered with this method will correctly respect the
+ * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+ * Prior to that, it would be ignored and delivered to all matching registered
+ * receivers. Be careful if using this for security.</p>
+ *
* <p class="note">Note: this method <em>cannot be called from a
* {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver
* that is declared in an application's manifest. It is okay, however, to call
@@ -1059,6 +1065,12 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+ * registered with this method will correctly respect the
+ * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+ * Prior to that, it would be ignored and delivered to all matching registered
+ * receivers. Be careful if using this for security.</p>
+ *
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
* @param broadcastPermission String naming a permissions that a
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index b7da472..7dbd6f2 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -22,5 +22,5 @@
/** @hide */
interface IIntentSender {
int send(int code, in Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver);
+ IIntentReceiver finishedReceiver, String requiredPermission);
}
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 007a715..4db4bdc 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -154,14 +154,47 @@
*/
public void sendIntent(Context context, int code, Intent intent,
OnFinished onFinished, Handler handler) throws SendIntentException {
+ sendIntent(context, code, intent, onFinished, handler, null);
+ }
+
+ /**
+ * Perform the operation associated with this IntentSender, allowing the
+ * caller to specify information about the Intent to use and be notified
+ * when the send has completed.
+ *
+ * @param context The Context of the caller. This may be null if
+ * <var>intent</var> is also null.
+ * @param code Result code to supply back to the IntentSender's target.
+ * @param intent Additional Intent data. See {@link Intent#fillIn
+ * Intent.fillIn()} for information on how this is applied to the
+ * original Intent. Use null to not modify the original Intent.
+ * @param onFinished The object to call back on when the send has
+ * completed, or null for no callback.
+ * @param handler Handler identifying the thread on which the callback
+ * should happen. If null, the callback will happen from the thread
+ * pool of the process.
+ * @param requiredPermission Name of permission that a recipient of the PendingIntent
+ * is required to hold. This is only valid for broadcast intents, and
+ * corresponds to the permission argument in
+ * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+ * If null, no permission is required.
+ *
+ *
+ * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void sendIntent(Context context, int code, Intent intent,
+ OnFinished onFinished, Handler handler, String requiredPermission)
+ throws SendIntentException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
int res = mTarget.send(code, intent, resolvedType,
onFinished != null
- ? new FinishedDispatcher(this, onFinished, handler)
- : null);
+ ? new FinishedDispatcher(this, onFinished, handler)
+ : null,
+ requiredPermission);
if (res < 0) {
throw new SendIntentException();
}
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 5ade9eb..0eb8cd8 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -338,7 +338,15 @@
* @hide
*/
public static NdefRecord createUri(Uri uri) {
- String uriString = uri.toString();
+ return createUri(uri.toString());
+ }
+
+ /**
+ * Creates an NDEF record of well known type URI.
+ * TODO: Make a public API
+ * @hide
+ */
+ public static NdefRecord createUri(String uriString) {
byte prefix = 0x0;
for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
if (uriString.startsWith(URI_PREFIX_MAP[i])) {
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 3971045..b492615 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -17,9 +17,6 @@
package android.provider;
-import com.android.internal.util.ArrayUtils;
-
-import android.accounts.Account;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentProviderClient;
@@ -35,13 +32,10 @@
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Log;
-import java.util.Arrays;
-
/**
* <p>
* The contract between the calendar provider and applications. Contains
@@ -97,6 +91,8 @@
/**
* Broadcast Action: This is the intent that gets fired when an alarm
* notification needs to be posted for a reminder.
+ *
+ * @SdkConstant
*/
public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
@@ -122,8 +118,7 @@
/**
* The content:// style URL for the top-level calendar authority
*/
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY);
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
/**
* An optional insert, update or delete URI parameter that allows the caller
@@ -572,29 +567,6 @@
* </ul>
*/
public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns {
- private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
- + " AND "
- + Calendars.ACCOUNT_TYPE + "=?";
-
- /**
- * Helper function for generating a calendars query. This is blocking
- * and should not be used on the UI thread. See
- * {@link ContentResolver#query(Uri, String[], String, String[], String)}
- * for more details about using the parameters.
- *
- * @param cr The ContentResolver to query with
- * @param projection A list of columns to return
- * @param selection A formatted selection string
- * @param selectionArgs arguments to the selection string
- * @param orderBy How to order the returned rows
- * @return
- */
- public static final Cursor query(ContentResolver cr, String[] projection, String selection,
- String[] selectionArgs, String orderBy) {
- return cr.query(CONTENT_URI, projection, selection, selectionArgs,
- orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
- }
-
/**
* The content:// style URL for accessing Calendars
*/
@@ -622,7 +594,9 @@
* These fields are only writable by a sync adapter. To modify them the
* caller must include {@link #CALLER_IS_SYNCADAPTER},
* {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the Uri's query
- * parameters.
+ * parameters. TODO move to provider
+ *
+ * @hide
*/
public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
ACCOUNT_NAME,
@@ -737,7 +711,7 @@
/**
* the projection used by the attendees query
*/
- private static final String[] PROJECTION = new String[] {
+ public static final String[] PROJECTION = new String[] {
_ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
@@ -1420,37 +1394,6 @@
CalendarColumns {
/**
- * Queries all events with the given projection. This is a blocking call
- * and should not be done on the UI thread.
- *
- * @param cr The content resolver to use for the query
- * @param projection The columns to return
- * @return A Cursor containing all events in the db
- */
- public static final Cursor query(ContentResolver cr, String[] projection) {
- return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
- }
-
- /**
- * Queries events using the given projection, selection filter, and
- * ordering. This is a blocking call and should not be done on the UI
- * thread. For selection and selectionArgs usage see
- * {@link ContentResolver#query(Uri, String[], String, String[], String)}
- *
- * @param cr The content resolver to use for the query
- * @param projection The columns to return
- * @param selection Filter on the query as an SQL WHERE statement
- * @param selectionArgs Args to replace any '?'s in the selection
- * @param orderBy How to order the rows as an SQL ORDER BY statement
- * @return A Cursor containing the matching events
- */
- public static final Cursor query(ContentResolver cr, String[] projection, String selection,
- String[] selectionArgs, String orderBy) {
- return cr.query(CONTENT_URI, projection, selection, null,
- orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
- }
-
- /**
* The content:// style URL for interacting with events. Appending an
* event id using {@link ContentUris#withAppendedId(Uri, long)} will
* specify a single event.
@@ -1464,7 +1407,7 @@
* appended event ID. Deletion of exceptions requires both the original event ID and
* the exception event ID (see {@link Uri.Builder#appendPath}).
*/
- public static final Uri EXCEPTION_CONTENT_URI =
+ public static final Uri CONTENT_EXCEPTION_URI =
Uri.parse("content://" + AUTHORITY + "/exception");
/**
@@ -1475,7 +1418,9 @@
/**
* These are columns that should only ever be updated by the provider,
* either because they are views mapped to another table or because they
- * are used for provider only functionality.
+ * are used for provider only functionality. TODO move to provider
+ *
+ * @hide
*/
public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
ACCOUNT_NAME,
@@ -1505,7 +1450,9 @@
/**
* These fields are only writable by a sync adapter. To modify them the
* caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
- * _SYNC_ACCOUNT_TYPE in the query parameters.
+ * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider.
+ *
+ * @hide
*/
public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
_SYNC_ID,
@@ -1672,11 +1619,6 @@
public static final String END_MINUTE = "endMinute";
}
- /**
- * CalendarCache stores some settings for calendar including the current
- * time zone for the instaces. These settings are stored using a key/value
- * scheme.
- */
protected interface CalendarCacheColumns {
/**
* The key for the setting. Keys are defined in {@link CalendarCache}.
@@ -1689,6 +1631,11 @@
public static final String VALUE = "value";
}
+ /**
+ * CalendarCache stores some settings for calendar including the current
+ * time zone for the instances. These settings are stored using a key/value
+ * scheme. A {@link #KEY} must be specified when updating these values.
+ */
public static class CalendarCache implements CalendarCacheColumns {
/**
* The URI to use for retrieving the properties from the Calendar db.
@@ -1697,22 +1644,11 @@
Uri.parse("content://" + AUTHORITY + "/properties");
/**
- * If updating a property, this must be provided as the selection. All
- * other selections will fail. For queries this field can be omitted to
- * retrieve all properties or used to query a single property. Valid
- * keys include {@link #TIMEZONE_KEY_TYPE},
- * {@link #TIMEZONE_KEY_INSTANCES}, and
- * {@link #TIMEZONE_KEY_INSTANCES_PREVIOUS}, though the last one can
- * only be read, not written.
- */
- public static final String WHERE = "key=?";
-
- /**
* They key for updating the use of auto/home time zones in Calendar.
* Valid values are {@link #TIMEZONE_TYPE_AUTO} or
* {@link #TIMEZONE_TYPE_HOME}.
*/
- public static final String TIMEZONE_KEY_TYPE = "timezoneType";
+ public static final String KEY_TIMEZONE_TYPE = "timezoneType";
/**
* The key for updating the time zone used by the provider when it
@@ -1720,24 +1656,24 @@
* type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id
* should be written to this field.
*/
- public static final String TIMEZONE_KEY_INSTANCES = "timezoneInstances";
+ public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances";
/**
* The key for reading the last time zone set by the user. This should
* only be read by apps and it will be automatically updated whenever
- * {@link #TIMEZONE_KEY_INSTANCES} is updated with
+ * {@link #KEY_TIMEZONE_INSTANCES} is updated with
* {@link #TIMEZONE_TYPE_HOME} set.
*/
- public static final String TIMEZONE_KEY_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
+ public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
/**
- * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider
+ * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
* should stay in sync with the device's time zone.
*/
public static final String TIMEZONE_TYPE_AUTO = "auto";
/**
- * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider
+ * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
* should use a fixed time zone set by the user.
*/
public static final String TIMEZONE_TYPE_HOME = "home";
@@ -1814,7 +1750,7 @@
/**
* The projection used by the EventDays query.
*/
- private static final String[] PROJECTION = {
+ public static final String[] PROJECTION = {
STARTDAY, ENDDAY
};
private static final String SELECTION = "selected=1";
@@ -1900,7 +1836,7 @@
/**
* The projection used by the reminders query.
*/
- private static final String[] PROJECTION = new String[] {
+ public static final String[] PROJECTION = new String[] {
_ID, MINUTES, METHOD,};
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
@@ -1967,17 +1903,28 @@
public static final String NOTIFY_TIME = "notifyTime";
/**
- * The state of this alert. It starts out as {@link #SCHEDULED}, then
- * when the alarm goes off, it changes to {@link #FIRED}, and then when
- * the user dismisses the alarm it changes to {@link #DISMISSED}. Column
+ * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then
+ * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when
+ * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column
* name.
* <P>Type: INTEGER</P>
*/
public static final String STATE = "state";
- public static final int SCHEDULED = 0;
- public static final int FIRED = 1;
- public static final int DISMISSED = 2;
+ /**
+ * An alert begins in this state when it is first created.
+ */
+ public static final int STATE_SCHEDULED = 0;
+ /**
+ * After a notification for an alert has been created it should be
+ * updated to fired.
+ */
+ public static final int STATE_FIRED = 1;
+ /**
+ * Once the user has dismissed the notification the alert's state should
+ * be set to dismissed so it is not fired again.
+ */
+ public static final int STATE_DISMISSED = 2;
/**
* The number of minutes that this alarm precedes the start time. Column
@@ -2024,7 +1971,7 @@
private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?";
private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC";
- private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + SCHEDULED
+ private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED
+ " AND " + ALARM_TIME + "<?"
+ " AND " + ALARM_TIME + ">?"
+ " AND " + END + ">=?";
@@ -2038,10 +1985,11 @@
public static final Uri CONTENT_URI_BY_INSTANCE =
Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance");
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
/**
- * Helper for inserting an alarm time associated with an event
+ * Helper for inserting an alarm time associated with an event TODO move
+ * to Provider
*
* @hide
*/
@@ -2056,51 +2004,32 @@
values.put(CalendarAlerts.CREATION_TIME, currentTime);
values.put(CalendarAlerts.RECEIVED_TIME, 0);
values.put(CalendarAlerts.NOTIFY_TIME, 0);
- values.put(CalendarAlerts.STATE, SCHEDULED);
+ values.put(CalendarAlerts.STATE, STATE_SCHEDULED);
values.put(CalendarAlerts.MINUTES, minutes);
return cr.insert(CONTENT_URI, values);
}
/**
- * Queries alerts info using the given projection, selection filter, and
- * ordering. This is a blocking call and should not be done on the UI
- * thread. For selection and selectionArgs usage see
- * {@link ContentResolver#query(Uri, String[], String, String[], String)}
- *
- * @param cr The content resolver to use for the query
- * @param projection The columns to return
- * @param selection Filter on the query as an SQL WHERE statement
- * @param selectionArgs Args to replace any '?'s in the selection
- * @param sortOrder How to order the rows as an SQL ORDER BY statement
- * @return A Cursor containing the matching alerts
- */
- public static final Cursor query(ContentResolver cr, String[] projection,
- String selection, String[] selectionArgs, String sortOrder) {
- return cr.query(CONTENT_URI, projection, selection, selectionArgs,
- sortOrder);
- }
-
- /**
* Finds the next alarm after (or equal to) the given time and returns
* the time of that alarm or -1 if no such alarm exists. This is a
- * blocking call and should not be done on the UI thread.
+ * blocking call and should not be done on the UI thread. TODO move to
+ * provider
*
* @param cr the ContentResolver
* @param millis the time in UTC milliseconds
* @return the next alarm time greater than or equal to "millis", or -1
* if no such alarm exists.
+ * @hide
*/
public static final long findNextAlarmTime(ContentResolver cr, long millis) {
String selection = ALARM_TIME + ">=" + millis;
// TODO: construct an explicit SQL query so that we can add
// "LIMIT 1" to the end and get just one result.
String[] projection = new String[] { ALARM_TIME };
- Cursor cursor = query(cr, projection,
- WHERE_FINDNEXTALARMTIME,
- new String[] {
+ Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME,
+ (new String[] {
Long.toString(millis)
- },
- SORT_ORDER_ALARMTIME_ASC);
+ }), SORT_ORDER_ALARMTIME_ASC);
long alarmTime = -1;
try {
if (cursor != null && cursor.moveToFirst()) {
@@ -2116,13 +2045,14 @@
/**
* Searches the CalendarAlerts table for alarms that should have fired
- * but have not and then reschedules them. This method can be called
- * at boot time to restore alarms that may have been lost due to a
- * phone reboot.
+ * but have not and then reschedules them. This method can be called at
+ * boot time to restore alarms that may have been lost due to a phone
+ * reboot. TODO move to provider
*
* @param cr the ContentResolver
* @param context the Context
* @param manager the AlarmManager
+ * @hide
*/
public static final void rescheduleMissedAlarms(ContentResolver cr,
Context context, AlarmManager manager) {
@@ -2136,15 +2066,10 @@
// TODO: construct an explicit SQL query so that we can add
// "GROUPBY" instead of doing a sort and de-dup
- Cursor cursor = CalendarAlerts.query(cr,
- projection,
- WHERE_RESCHEDULE_MISSED_ALARMS,
- new String[] {
- Long.toString(now),
- Long.toString(ancient),
- Long.toString(now)
- },
- SORT_ORDER_ALARMTIME_ASC);
+ Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection,
+ WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] {
+ Long.toString(now), Long.toString(ancient), Long.toString(now)
+ }), SORT_ORDER_ALARMTIME_ASC);
if (cursor == null) {
return;
}
@@ -2177,12 +2102,13 @@
* keep scheduled reminders up to date but apps may use this to
* implement snooze functionality without modifying the reminders table.
* Scheduled alarms will generate an intent using
- * {@link #ACTION_EVENT_REMINDER}.
+ * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider
*
* @param context A context for referencing system resources
* @param manager The AlarmManager to use or null
* @param alarmTime The time to fire the intent in UTC millis since
* epoch
+ * @hide
*/
public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
if (DEBUG) {
@@ -2204,31 +2130,28 @@
}
/**
- * Searches for an entry in the CalendarAlerts table that matches
- * the given event id, begin time and alarm time. If one is found
- * then this alarm already exists and this method returns true.
- *
+ * Searches for an entry in the CalendarAlerts table that matches the
+ * given event id, begin time and alarm time. If one is found then this
+ * alarm already exists and this method returns true. TODO Move to
+ * provider
+ *
* @param cr the ContentResolver
* @param eventId the event id to match
* @param begin the start time of the event in UTC millis
* @param alarmTime the alarm time of the event in UTC millis
- * @return true if there is already an alarm for the given event
- * with the same start time and alarm time.
+ * @return true if there is already an alarm for the given event with
+ * the same start time and alarm time.
+ * @hide
*/
public static final boolean alarmExists(ContentResolver cr, long eventId,
long begin, long alarmTime) {
// TODO: construct an explicit SQL query so that we can add
// "LIMIT 1" to the end and get just one result.
String[] projection = new String[] { ALARM_TIME };
- Cursor cursor = query(cr,
- projection,
- WHERE_ALARM_EXISTS,
- new String[] {
- Long.toString(eventId),
- Long.toString(begin),
- Long.toString(alarmTime)
- },
- null);
+ Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS,
+ (new String[] {
+ Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime)
+ }), null);
boolean found = false;
try {
if (cursor != null && cursor.getCount() > 0) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 4107c5a..aae9ccf 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -266,7 +266,7 @@
}
}
- Alignment align = mAlignment;
+ Alignment paraAlign = mAlignment;
TabStops tabStops = null;
boolean tabStopsIsInitialized = false;
@@ -310,10 +310,10 @@
ParagraphStyle.class);
spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
- align = mAlignment;
+ paraAlign = mAlignment;
for (int n = spans.length-1; n >= 0; n--) {
if (spans[n] instanceof AlignmentSpan) {
- align = ((AlignmentSpan) spans[n]).getAlignment();
+ paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
break;
}
}
@@ -360,6 +360,16 @@
tabStopsIsInitialized = true;
}
+ // Determine whether the line aligns to normal, opposite, or center.
+ Alignment align = paraAlign;
+ if (align == Alignment.ALIGN_LEFT) {
+ align = (dir == DIR_LEFT_TO_RIGHT) ?
+ Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
+ } else if (align == Alignment.ALIGN_RIGHT) {
+ align = (dir == DIR_LEFT_TO_RIGHT) ?
+ Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
+ }
+
int x;
if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_LEFT_TO_RIGHT) {
@@ -411,7 +421,9 @@
int dir = getParagraphDirection(line);
int x;
- if (align == Alignment.ALIGN_NORMAL) {
+ if (align == Alignment.ALIGN_LEFT) {
+ x = left;
+ } else if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = left;
} else {
@@ -430,7 +442,9 @@
}
}
int max = (int)getLineExtent(line, tabStops, false);
- if (align == Alignment.ALIGN_OPPOSITE) {
+ if (align == Alignment.ALIGN_RIGHT) {
+ x = right - max;
+ } else if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = right - max;
} else {
@@ -738,11 +752,15 @@
int dir = getParagraphDirection(line);
Alignment align = getParagraphAlignment(line);
- if (align == Alignment.ALIGN_NORMAL) {
+ if (align == Alignment.ALIGN_LEFT) {
+ return 0;
+ } else if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_RIGHT_TO_LEFT)
return getParagraphRight(line) - getLineMax(line);
else
return 0;
+ } else if (align == Alignment.ALIGN_RIGHT) {
+ return mWidth - getLineMax(line);
} else if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_RIGHT_TO_LEFT)
return 0;
@@ -765,11 +783,15 @@
int dir = getParagraphDirection(line);
Alignment align = getParagraphAlignment(line);
- if (align == Alignment.ALIGN_NORMAL) {
+ if (align == Alignment.ALIGN_LEFT) {
+ return getParagraphLeft(line) + getLineMax(line);
+ } else if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_RIGHT_TO_LEFT)
return mWidth;
else
return getParagraphLeft(line) + getLineMax(line);
+ } else if (align == Alignment.ALIGN_RIGHT) {
+ return mWidth;
} else if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_RIGHT_TO_LEFT)
return getLineMax(line);
@@ -1765,8 +1787,10 @@
ALIGN_NORMAL,
ALIGN_OPPOSITE,
ALIGN_CENTER,
- // XXX ALIGN_LEFT,
- // XXX ALIGN_RIGHT,
+ /** @hide */
+ ALIGN_LEFT,
+ /** @hide */
+ ALIGN_RIGHT,
}
private static final int TAB_INCREMENT = 20;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bf7f359..bb5c954 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9121,9 +9121,15 @@
}
/**
- * Reset the resolved layout direction by clearing the corresponding flag
+ * Reset the resolved layout direction.
+ *
+ * Subclasses need to override this method to clear cached information that depends on the
+ * resolved layout direction, or to inform child views that inherit their layout direction.
+ * Overrides must also call the superclass implementation at the start of their implementation.
+ *
+ * @hide
*/
- void resetLayoutDirectionResolution() {
+ protected void resetLayoutDirectionResolution() {
// Reset the current View resolution
mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2a90dde..da88fbb 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1746,6 +1746,7 @@
final long now = SystemClock.uptimeMillis();
event = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
syntheticEvent = true;
}
@@ -4998,12 +4999,8 @@
viewAncestor.requestTransitionStart(transition);
}
- /**
- * This method will be called when we need to reset the layout direction resolution flag
- *
- */
@Override
- void resetLayoutDirectionResolution() {
+ protected void resetLayoutDirectionResolution() {
super.resetLayoutDirectionResolution();
// Take care of resetting the children resolution too
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 25f01a7..ac86769 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -25,40 +25,51 @@
import java.util.List;
/**
+ * <p>
* This class represents accessibility events that are sent by the system when
* something notable happens in the user interface. For example, when a
* {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
+ * </p>
* <p>
* An accessibility event is fired by an individual view which populates the event with
- * a record for its state and requests from its parent to send the event to interested
- * parties. The parent can optionally add a record for itself before dispatching a similar
- * request to its parent. A parent can also choose not to respect the request for sending
- * an event. The accessibility event is sent by the topmost view in the view tree.
- * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore
- * all records in an accessibility event to obtain more information about the context
- * in which the event was fired.
+ * data for its state and requests from its parent to send the event to interested
+ * parties. The parent can optionally add an {@link AccessibilityRecord} for itself before
+ * dispatching a similar request to its parent. A parent can also choose not to respect the
+ * request for sending an event. The accessibility event is sent by the topmost view in the
+ * view tree. Therefore, an {@link android.accessibilityservice.AccessibilityService} can
+ * explore all records in an accessibility event to obtain more information about the
+ * context in which the event was fired.
+ * </p>
* <p>
- * A client can add, remove, and modify records. The getters and setters for individual
- * properties operate on the current record which can be explicitly set by the client. By
- * default current is the first record. Thus, querying a record would require setting
- * it as the current one and interacting with the property getters and setters.
+ * The main purpose of an accessibility event is to expose enough information for an
+ * {@link android.accessibilityservice.AccessibilityService} to provide meaningful feedback
+ * to the user. Sometimes however, an accessibility service may need more contextual
+ * information then the one in the event pay-load. In such cases the service can obtain
+ * the event source which is an {@link AccessibilityNodeInfo} (snapshot of a View state)
+ * which can be used for exploring the window content. Note that the privilege for accessing
+ * an event's source, thus the window content, has to be explicitly requested. For more
+ * details refer to {@link android.accessibilityservice.AccessibilityService}. If an
+ * accessibility service has not requested to retrieve the window content the event will
+ * not contain reference to its source. Also for events of type
+ * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available.
+ * </p>
* <p>
* This class represents various semantically different accessibility event
- * types. Each event type has associated a set of related properties. In other
+ * types. Each event type has an associated set of related properties. In other
* words, each event type is characterized via a subset of the properties exposed
* by this class. For each event type there is a corresponding constant defined
- * in this class. Since some event types are semantically close there are mask
- * constants that group them together. Follows a specification of the event
- * types and their associated properties:
+ * in this class. Follows a specification of the event types and their associated properties:
+ * </p>
* <p>
- * <b>VIEW TYPES</b> <br>
+ * <b>VIEW TYPES</b></br>
+ * </p>
* <p>
* <b>View clicked</b> - represents the event of clicking on a {@link android.view.View}
- * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
- * Type:{@link #TYPE_VIEW_CLICKED} <br>
- * Properties:</br>
+ * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.</br>
+ * <em>Type:</em>{@link #TYPE_VIEW_CLICKED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -67,13 +78,14 @@
* <li>{@link #isPassword()} - Whether the source is password.</li>
* <li>{@link #isChecked()} - Whether the source is checked.</li>
* </ul>
+ * </p>
* <p>
* <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View}
- * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
- * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br>
- * Properties:</br>
+ * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc </br>
+ * <em>Type:</em>{@link #TYPE_VIEW_LONG_CLICKED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -82,13 +94,14 @@
* <li>{@link #isPassword()} - Whether the source is password.</li>
* <li>{@link #isChecked()} - Whether the source is checked.</li>
* </ul>
+ * </p>
* <p>
* <b>View selected</b> - represents the event of selecting an item usually in
- * the context of an {@link android.widget.AdapterView}. <br>
- * Type: {@link #TYPE_VIEW_SELECTED} <br>
- * Properties:</br>
+ * the context of an {@link android.widget.AdapterView}.</br>
+ * <em>Type:</em> {@link #TYPE_VIEW_SELECTED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -96,17 +109,17 @@
* <li>{@link #isEnabled()} - Whether the source is enabled.</li>
* <li>{@link #isPassword()} - Whether the source is password.</li>
* <li>{@link #isChecked()} - Whether the source is checked.</li>
- * <li>{@link #getItemCount()} -The number of selectable items of the source.</li>
+ * <li>{@link #getItemCount()} - The number of selectable items of the source.</li>
* <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li>
* </ul>
- * <p>
+ * </p>
* <p>
* <b>View focused</b> - represents the event of focusing a
- * {@link android.view.View}. <br>
- * Type: {@link #TYPE_VIEW_FOCUSED} <br>
- * Properties:</br>
+ * {@link android.view.View}.</br>
+ * <em>Type:</em> {@link #TYPE_VIEW_FOCUSED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -114,16 +127,17 @@
* <li>{@link #isEnabled()} - Whether the source is enabled.</li>
* <li>{@link #isPassword()} - Whether the source is password.</li>
* <li>{@link #isChecked()} - Whether the source is checked.</li>
- * <li>{@link #getItemCount()} -The number of focusable items on the screen.</li>
+ * <li>{@link #getItemCount()} - The number of focusable items on the screen.</li>
* <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li>
* </ul>
+ * </p>
* <p>
* <b>View text changed</b> - represents the event of changing the text of an
- * {@link android.widget.EditText}. <br>
- * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br>
- * Properties:</br>
+ * {@link android.widget.EditText}.</br>
+ * <em>Type:</em> {@link #TYPE_VIEW_TEXT_CHANGED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -136,13 +150,14 @@
* <li>{@link #getRemovedCount()} - The number of removed characters.</li>
* <li>{@link #getBeforeText()} - The text of the source before the change.</li>
* </ul>
+ * </p>
* <p>
* <b>View text selection changed</b> - represents the event of changing the text
- * selection of an {@link android.widget.EditText}.<br>
- * Type: {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} <br>
- * Properties:</br>
+ * selection of an {@link android.widget.EditText}.</br>
+ * <em>Type:</em> {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} </br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -152,7 +167,8 @@
* <li>{@link #getFromIndex()} - The selection start index.</li>
* <li>{@link #getToIndex()} - The selection end index.</li>
* <li>{@link #getItemCount()} - The length of the source text.</li>
- * <ul>
+ * </ul>
+ * </p>
* <p>
* <b>View scrolled</b> - represents the event of scrolling a view. If
* the source is a descendant of {@link android.widget.AdapterView} the
@@ -161,11 +177,11 @@
* is unaware if its pixel size since its adapter is responsible for
* creating views. In all other cases the scroll is reported as the current
* scroll on the X and Y axis respectively plus the height of the source in
- * pixels.<br>
- * Type: {@link #TYPE_VIEW_SCROLLED} <br>
- * Properties:</br>
+ * pixels.</br>
+ * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
@@ -181,41 +197,49 @@
* (for descendants of AdapterView).</li>
* <li>{@link #getItemCount()} - The total items of the source (for descendants of AdapterView)
* or the height of the source in pixels (all other cases).</li>
- * <ul>
+ * </ul>
+ * </p>
* <p>
- * <b>TRANSITION TYPES</b> <br>
- * <p>
+ * <b>TRANSITION TYPES</b></br>
+ * </p>
* <b>Window state changed</b> - represents the event of opening a
* {@link android.widget.PopupWindow}, {@link android.view.Menu},
- * {@link android.app.Dialog}, etc. <br>
- * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br>
- * Properties:</br>
+ * {@link android.app.Dialog}, etc.</br>
+ * <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
* <li>{@link #getText()} - The text of the source.</li>
* </ul>
+ * </p>
* <p>
* <b>Window content changed</b> - represents the event of change in the
* content of a window. This change can be adding/removing view, changing
- * a view size, etc.<br>
- * Type: {@link #TYPE_WINDOW_CONTENT_CHANGED} <br>
- * Properties:</br>
+ * a view size, etc.</br>
+ * <p>
+ * <strong>Note:</strong> This event is fired only for the window source of the
+ * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED})
+ * and its purpose is to notify clients that the content of the user interaction
+ * window has changed.
+ * </p>
+ * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br>
+ * <em>Properties:</em></br>
* <ul>
- * <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
- * <ul>
+ * </ul>
* <p>
- * <b>NOTIFICATION TYPES</b> <br>
+ * <b>NOTIFICATION TYPES</b></br>
* <p>
- * <b>Notification state changed</b> - represents the event showing/hiding
+ * <b>Notification state changed</b> - represents the event showing
* {@link android.app.Notification}.
- * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br>
- * Properties:</br>
+ * <em>Type:</em> {@link #TYPE_NOTIFICATION_STATE_CHANGED}</br>
+ * <em>Properties:</em></br>
* <ul>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
@@ -223,15 +247,17 @@
* <li>{@link #getText()} - The text of the source.</li>
* <li>{@link #getParcelableData()} - The posted {@link android.app.Notification}.</li>
* </ul>
+ * </p>
* <p>
* <b>Security note</b>
* <p>
- * Since an event contains the text of its source privacy can be compromised by leaking of
+ * Since an event contains the text of its source privacy can be compromised by leaking
* sensitive information such as passwords. To address this issue any event fired in response
* to manipulation of a PASSWORD field does NOT CONTAIN the text of the password.
*
* @see android.view.accessibility.AccessibilityManager
* @see android.accessibilityservice.AccessibilityService
+ * @see AccessibilityNodeInfo
*/
public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
private static final boolean DEBUG = false;
@@ -285,13 +311,13 @@
public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
/**
- * Represents the event of opening/closing a {@link android.widget.PopupWindow},
+ * Represents the event of opening a {@link android.widget.PopupWindow},
* {@link android.view.Menu}, {@link android.app.Dialog}, etc.
*/
public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
/**
- * Represents the event showing/hiding a {@link android.app.Notification}.
+ * Represents the event showing a {@link android.app.Notification}.
*/
public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
@@ -340,6 +366,13 @@
* @see #TYPE_VIEW_TEXT_CHANGED
* @see #TYPE_WINDOW_STATE_CHANGED
* @see #TYPE_NOTIFICATION_STATE_CHANGED
+ * @see #TYPE_VIEW_HOVER_ENTER
+ * @see #TYPE_VIEW_HOVER_EXIT
+ * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START
+ * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END
+ * @see #TYPE_WINDOW_CONTENT_CHANGED
+ * @see #TYPE_VIEW_SCROLLED
+ * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -432,10 +465,10 @@
}
/**
- * Gets the records at a given index.
+ * Gets the record at a given index.
*
* @param index The index.
- * @return The records at the specified index.
+ * @return The record at the specified index.
*/
public AccessibilityRecord getRecord(int index) {
return mRecords.get(index);
@@ -506,7 +539,7 @@
/**
* Returns a cached instance if such is available or a new one is
- * instantiated with type property set.
+ * instantiated with its type property set.
*
* @param eventType The event type.
* @return An instance.
@@ -519,7 +552,7 @@
/**
* Returns a cached instance if such is available or a new one is
- * instantiated with type property set.
+ * initialized with from the given <code>event</code>.
*
* @param event The other event.
* @return An instance.
@@ -559,9 +592,10 @@
}
/**
- * Return an instance back to be reused.
+ * Recycles an instance back to be reused.
* <p>
- * <b>Note: You must not touch the object after calling this function.</b>
+ * <b>Note: You must not touch the object after calling this function.</b>
+ * </p>
*
* @throws IllegalStateException If the event is already recycled.
*/
@@ -714,7 +748,7 @@
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append("; EventType: ").append(eventTypeToString(mEventType));
+ builder.append("EventType: ").append(eventTypeToString(mEventType));
builder.append("; EventTime: ").append(mEventTime);
builder.append("; PackageName: ").append(mPackageName);
builder.append(super.toString());
@@ -758,11 +792,11 @@
* Returns the string representation of an event type. For example,
* {@link #TYPE_VIEW_CLICKED} is represented by the string TYPE_VIEW_CLICKED.
*
- * @param feedbackType The event type
+ * @param eventType The event type
* @return The string representation.
*/
- public static String eventTypeToString(int feedbackType) {
- switch (feedbackType) {
+ public static String eventTypeToString(int eventType) {
+ switch (eventType) {
case TYPE_VIEW_CLICKED:
return "TYPE_VIEW_CLICKED";
case TYPE_VIEW_LONG_CLICKED:
diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java
index 3d70959..f11880b 100644
--- a/core/java/android/view/accessibility/AccessibilityEventSource.java
+++ b/core/java/android/view/accessibility/AccessibilityEventSource.java
@@ -24,11 +24,12 @@
/**
* Handles the request for sending an {@link AccessibilityEvent} given
* the event type. The method must first check if accessibility is on
- * via calling {@link AccessibilityManager#isEnabled()}, obtain
- * an {@link AccessibilityEvent} from the event pool through calling
- * {@link AccessibilityEvent#obtain(int)}, populate the event, and
- * send it for dispatch via calling
- * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}.
+ * via calling {@link AccessibilityManager#isEnabled() AccessibilityManager.isEnabled()},
+ * obtain an {@link AccessibilityEvent} from the event pool through calling
+ * {@link AccessibilityEvent#obtain(int) AccessibilityEvent.obtain(int)}, populate the
+ * event, and send it for dispatch via calling
+ * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)
+ * AccessibilityManager.sendAccessibilityEvent(AccessibilityEvent)}.
*
* @see AccessibilityEvent
* @see AccessibilityManager
@@ -41,7 +42,8 @@
* Handles the request for sending an {@link AccessibilityEvent}. The
* method does not guarantee to check if accessibility is on before
* sending the event for dispatch. It is responsibility of the caller
- * to do the check via calling {@link AccessibilityManager#isEnabled()}.
+ * to do the check via calling {@link AccessibilityManager#isEnabled()
+ * AccessibilityManager.isEnabled()}.
*
* @see AccessibilityEvent
* @see AccessibilityManager
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index eece64a..314b7ca 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -37,16 +37,30 @@
import java.util.concurrent.CopyOnWriteArrayList;
/**
- * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
- * Such events are generated when something notable happens in the user interface,
+ * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
+ * and provides facilities for querying the accessibility state of the system.
+ * Accessibility events are generated when something notable happens in the user interface,
* for example an {@link android.app.Activity} starts, the focus or selection of a
* {@link android.view.View} changes etc. Parties interested in handling accessibility
* events implement and register an accessibility service which extends
* {@link android.accessibilityservice.AccessibilityService}.
+ * <p>
+ * To obtain a handle to the accessibility manager do the following:
+ * </p>
+ * <p>
+ * <code>
+ * <pre>
+ * AccessibilityManager accessibilityManager =
+ * (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ * </pre>
+ * </code>
+ * </p>
*
* @see AccessibilityEvent
+ * @see AccessibilityNodeInfo
* @see android.accessibilityservice.AccessibilityService
- * @see android.content.Context#getSystemService
+ * @see Context#getSystemService
+ * @see Context#ACCESSIBILITY_SERVICE
*/
public final class AccessibilityManager {
private static final boolean DEBUG = false;
@@ -72,10 +86,11 @@
* Listener for the accessibility state.
*/
public interface AccessibilityStateChangeListener {
+
/**
* Called back on change in the accessibility state.
*
- * @param enabled
+ * @param enabled Whether accessibility is enabled.
*/
public void onAccessibilityStateChanged(boolean enabled);
}
@@ -142,9 +157,9 @@
}
/**
- * Returns if the {@link AccessibilityManager} is enabled.
+ * Returns if the accessibility in the system is enabled.
*
- * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
+ * @return True if accessibility is enabled, false otherwise.
*/
public boolean isEnabled() {
synchronized (mHandler) {
@@ -161,17 +176,15 @@
* @hide
*/
public IAccessibilityManagerClient getClient() {
- return (IAccessibilityManagerClient) mClient.asBinder();
+ return (IAccessibilityManagerClient) mClient.asBinder();
}
/**
- * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not
- * enabled the call is a NOOP.
+ * Sends an {@link AccessibilityEvent}.
*
- * @param event The {@link AccessibilityEvent}.
+ * @param event The event to send.
*
- * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent}
- * while accessibility is not enabled.
+ * @throws IllegalStateException if accessibility is not enabled.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
if (!mIsEnabled) {
@@ -199,7 +212,7 @@
}
/**
- * Requests interruption of the accessibility feedback from all accessibility services.
+ * Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
if (!mIsEnabled) {
@@ -256,13 +269,20 @@
* Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
* for a given feedback type.
*
- * @param feedbackType The feedback type (can be bitwise or of multiple types).
+ * @param feedbackTypeFlags The feedback type flags.
* @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+ *
+ * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+ * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+ * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+ * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+ * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
*/
- public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+ int feedbackTypeFlags) {
List<AccessibilityServiceInfo> services = null;
try {
- services = mService.getEnabledAccessibilityServiceList(feedbackType);
+ services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags);
if (DEBUG) {
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
@@ -273,7 +293,8 @@
}
/**
- * Registers an {@link AccessibilityStateChangeListener}.
+ * Registers an {@link AccessibilityStateChangeListener} for changes in
+ * the global accessibility state of the system.
*
* @param listener The listener.
* @return True if successfully registered.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index dbbe7be..031c6ae 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -22,7 +22,6 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;
-import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.View;
@@ -30,12 +29,26 @@
import java.util.List;
/**
- * This class represents a node of the screen content. From the point of
- * view of an accessibility service the screen content is presented as tree
- * of accessibility nodes.
+ * This class represents a node of the window content as well as actions that
+ * can be requested from its source. From the point of view of an
+ * {@link android.accessibilityservice.AccessibilityService} a window content is
+ * presented as tree of accessibility node info which may or may not map one-to-one
+ * to the view hierarchy. In other words, a custom view is free to report itself as
+ * a tree of accessibility node info.
+ * </p>
+ * <p>
+ * Once an accessibility node info is delivered to an accessibility service it is
+ * made immutable and calling a state mutation method generates an error.
+ * </p>
+ * <p>
+ * Please refer to {@link android.accessibilityservice.AccessibilityService} for
+ * details about how to obtain a handle to window content as a tree of accessibility
+ * node info as well as familiarizing with the security model.
+ * </p>
*
- * TODO(svertoslavganov): Update the documentation, add sample, and describe
- * the security policy.
+ * @see android.accessibilityservice.AccessibilityService
+ * @see AccessibilityEvent
+ * @see AccessibilityManager
*/
public class AccessibilityNodeInfo implements Parcelable {
@@ -85,9 +98,6 @@
private static final int PROPERTY_SCROLLABLE = 0x00000200;
- // Readable representations - lazily initialized.
- private static SparseArray<String> sActionSymbolicNames;
-
// Housekeeping.
private static final int MAX_POOL_SIZE = 50;
private static final Object sPoolLock = new Object();
@@ -154,12 +164,11 @@
/**
* Get the child at given index.
* <p>
- * <strong>
- * It is a client responsibility to recycle the received info by
- * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
- * of multiple instances.
- * </strong>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
* </p>
+ *
* @param index The child index.
* @return The child node.
*
@@ -184,9 +193,11 @@
/**
* Adds a child.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param child The child.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -215,9 +226,11 @@
/**
* Adds an action that can be performed on the node.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param action The action.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -230,9 +243,10 @@
/**
* Performs an action on the node.
* <p>
- * Note: An action can be performed only if the request is made
+ * <strong>Note:</strong> An action can be performed only if the request is made
* from an {@link android.accessibilityservice.AccessibilityService}.
* </p>
+ *
* @param action The action to perform.
* @return True if the action was performed.
*
@@ -256,6 +270,11 @@
* Finds {@link AccessibilityNodeInfo}s by text. The match is case
* insensitive containment. The search is relative to this info i.e.
* this info is the root of the traversed tree.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
*
* @param text The searched text.
* @return A list of node info.
@@ -277,12 +296,11 @@
/**
* Gets the unique id identifying this node's parent.
* <p>
- * <strong>
- * It is a client responsibility to recycle the received info by
- * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
- * of multiple instances.
- * </strong>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
* </p>
+ *
* @return The node's patent id.
*/
public AccessibilityNodeInfo getParent() {
@@ -302,9 +320,11 @@
/**
* Sets the parent.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param parent The parent.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -327,9 +347,11 @@
/**
* Sets the node bounds in parent coordinates.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param bounds The node bounds.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -352,9 +374,11 @@
/**
* Sets the node bounds in screen coordinates.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param bounds The node bounds.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -376,9 +400,11 @@
/**
* Sets whether this node is checkable.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param checkable True if the node is checkable.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -399,9 +425,11 @@
/**
* Sets whether this node is checked.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param checked True if the node is checked.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -422,9 +450,11 @@
/**
* Sets whether this node is focusable.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param focusable True if the node is focusable.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -445,9 +475,11 @@
/**
* Sets whether this node is focused.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param focused True if the node is focused.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -468,9 +500,11 @@
/**
* Sets whether this node is selected.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param selected True if the node is selected.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -491,9 +525,11 @@
/**
* Sets whether this node is clickable.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param clickable True if the node is clickable.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -514,9 +550,11 @@
/**
* Sets whether this node is long clickable.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param longClickable True if the node is long clickable.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -537,9 +575,11 @@
/**
* Sets whether this node is enabled.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param enabled True if the node is enabled.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -560,9 +600,11 @@
/**
* Sets whether this node is a password.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param password True if the node is a password.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -582,6 +624,11 @@
/**
* Sets if the node is scrollable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
*
* @param scrollable True if the node is scrollable, false otherwise.
*
@@ -604,9 +651,11 @@
/**
* Sets the package this node comes from.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param packageName The package name.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -628,9 +677,11 @@
/**
* Sets the class this node comes from.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param className The class name.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -652,9 +703,11 @@
/**
* Sets the text of this node.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param text The text.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -676,9 +729,11 @@
/**
* Sets the content description of this node.
* <p>
- * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
+ *
* @param contentDescription The content description.
*
* @throws IllegalStateException If called from an AccessibilityService.
@@ -820,7 +875,7 @@
/**
* Return an instance back to be reused.
* <p>
- * <b>Note: You must not touch the object after calling this function.</b>
+ * <strong>Note:</strong> You must not touch the object after calling this function.
*
* @throws IllegalStateException If the info is already recycled.
*/
@@ -842,8 +897,8 @@
/**
* {@inheritDoc}
* <p>
- * <b>Note: After the instance is written to a parcel it is recycled.
- * You must not touch the object after calling this function.</b>
+ * <strong>Note:</strong> After the instance is written to a parcel it
+ * is recycled. You must not touch the object after calling this function.
* </p>
*/
public void writeToParcel(Parcel parcel, int flags) {
@@ -885,7 +940,7 @@
TextUtils.writeToParcel(mContentDescription, parcel, flags);
// Since instances of this class are fetched via synchronous i.e. blocking
- // calls in IPCs and we always recycle as soon as the instance is marshaled.
+ // calls in IPCs we always recycle as soon as the instance is marshaled.
recycle();
}
@@ -957,15 +1012,18 @@
* @return The symbolic name.
*/
private static String getActionSymbolicName(int action) {
- SparseArray<String> actionSymbolicNames = sActionSymbolicNames;
- if (actionSymbolicNames == null) {
- actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>();
- actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS");
- actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS");
- actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT");
- actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT");
+ switch (action) {
+ case ACTION_FOCUS:
+ return "ACTION_FOCUS";
+ case ACTION_CLEAR_FOCUS:
+ return "ACTION_CLEAR_FOCUS";
+ case ACTION_SELECT:
+ return "ACTION_SELECT";
+ case ACTION_CLEAR_SELECTION:
+ return "ACTION_CLEAR_SELECTION";
+ default:
+ throw new IllegalArgumentException("Unknown action: " + action);
}
- return actionSymbolicNames.get(action);
}
private boolean canPerformRequestOverConnection(int accessibilityViewId) {
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index b9815c5..f4d5e89 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -25,12 +25,28 @@
import java.util.List;
/**
- * Represents a record in an accessibility event. This class encapsulates
- * the information for a {@link android.view.View}. Note that not all properties
- * are applicable to all view types. For detailed information please refer to
- * {@link AccessibilityEvent}.
+ * Represents a record in an {@link AccessibilityEvent} and contains information
+ * about state change of its source {@link android.view.View}. When a view fires
+ * an accessibility event it requests from its parent to dispatch the
+ * constructed event. The parent may optionally append a record for itself
+ * for providing more context to
+ * {@link android.accessibilityservice.AccessibilityService}s. Hence,
+ * accessibility services can facilitate additional accessibility records
+ * to enhance feedback.
+ * </p>
+ * <p>
+ * Once the accessibility event containing a record is dispatched the record is
+ * made immutable and calling a state mutation method generates an error.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Not all properties are applicable to all accessibility
+ * event types. For detailed information please refer to {@link AccessibilityEvent}.
+ * </p>
*
* @see AccessibilityEvent
+ * @see AccessibilityManager
+ * @see android.accessibilityservice.AccessibilityService
+ * @see AccessibilityNodeInfo
*/
public class AccessibilityRecord {
@@ -79,32 +95,6 @@
}
/**
- * Initialize this record from another one.
- *
- * @param record The to initialize from.
- */
- void init(AccessibilityRecord record) {
- mSealed = record.mSealed;
- mBooleanProperties = record.mBooleanProperties;
- mCurrentItemIndex = record.mCurrentItemIndex;
- mItemCount = record.mItemCount;
- mFromIndex = record.mFromIndex;
- mToIndex = record.mToIndex;
- mScrollX = record.mScrollX;
- mScrollY = record.mScrollY;
- mAddedCount = record.mAddedCount;
- mRemovedCount = record.mRemovedCount;
- mClassName = record.mClassName;
- mContentDescription = record.mContentDescription;
- mBeforeText = record.mBeforeText;
- mParcelableData = record.mParcelableData;
- mText.addAll(record.mText);
- mSourceWindowId = record.mSourceWindowId;
- mSourceViewId = record.mSourceViewId;
- mConnection = record.mConnection;
- }
-
- /**
* Sets the event source.
*
* @param source The source.
@@ -125,13 +115,12 @@
/**
* Gets the {@link AccessibilityNodeInfo} of the event source.
* <p>
- * <strong>
- * It is a client responsibility to recycle the received info by
- * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
- * of multiple instances.
- * </strong>
+ * <strong>Note:</strong> It is a client responsibility to recycle the received info
+ * by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
+ *
* </p>
- * @return The info.
+ * @return The info of the source.
*/
public AccessibilityNodeInfo getSource() {
enforceSealed();
@@ -641,7 +630,7 @@
/**
* Return an instance back to be reused.
* <p>
- * <b>Note: You must not touch the object after calling this function.</b>
+ * <strong>Note:</strong> You must not touch the object after calling this function.
*
* @throws IllegalStateException If the record is already recycled.
*/
@@ -661,6 +650,32 @@
}
/**
+ * Initialize this record from another one.
+ *
+ * @param record The to initialize from.
+ */
+ void init(AccessibilityRecord record) {
+ mSealed = record.mSealed;
+ mBooleanProperties = record.mBooleanProperties;
+ mCurrentItemIndex = record.mCurrentItemIndex;
+ mItemCount = record.mItemCount;
+ mFromIndex = record.mFromIndex;
+ mToIndex = record.mToIndex;
+ mScrollX = record.mScrollX;
+ mScrollY = record.mScrollY;
+ mAddedCount = record.mAddedCount;
+ mRemovedCount = record.mRemovedCount;
+ mClassName = record.mClassName;
+ mContentDescription = record.mContentDescription;
+ mBeforeText = record.mBeforeText;
+ mParcelableData = record.mParcelableData;
+ mText.addAll(record.mText);
+ mSourceWindowId = record.mSourceWindowId;
+ mSourceViewId = record.mSourceViewId;
+ mConnection = record.mConnection;
+ }
+
+ /**
* Clears the state of this instance.
*/
void clear() {
diff --git a/core/java/android/view/accessibility/package.html b/core/java/android/view/accessibility/package.html
new file mode 100644
index 0000000..4afafd3
--- /dev/null
+++ b/core/java/android/view/accessibility/package.html
@@ -0,0 +1,39 @@
+<html>
+<body>
+<p>
+ The classes in this package are used to represent screen content and changes to it
+ as well as APIs for querying the global accessibility state of the system.
+</p>
+<p>
+ {@link android.view.accessibility.AccessibilityEvent}s are sent by the system when
+ something notable happens in the user interface. For example, when a
+ {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
+</p>
+<p>
+ {@link android.view.accessibility.AccessibilityRecord} contains information
+ about state change of its source {@link android.view.View}. When a view fires
+ an accessibility event it requests from its parent to dispatch the
+ constructed event. The parent may optionally append a record for itself for
+ providing more context to {@link android.accessibilityservice.AccessibilityService}s.
+ Hence, accessibility services can facilitate additional accessibility records
+ to enhance feedback.
+</p>
+<p>
+ {@link android.view.accessibility.AccessibilityNodeInfo} represents a node of the
+ window content as well as actions that can be requested from its source. From the point
+ of view of an {@link android.accessibilityservice.AccessibilityService} a window content is
+ presented as tree of accessibility node info which may or may not map one-to-one
+ to the view hierarchy. In other words, a custom view is free to report itself as
+ a tree of accessibility node info.
+</p>
+<p>
+ {@link android.view.accessibility.AccessibilityManager} is a system level service that
+ serves as an event dispatch for {@link android.view.accessibility.AccessibilityEvent}s,
+ and provides facilities for querying the accessibility state of the system. Accessibility
+ events are generated when something notable happens in the user interface, for example an
+ {@link android.app.Activity} starts, the focus or selection of a {@link android.view.View}
+ changes etc. Parties interested in handling accessibility events implement and register an
+ accessibility service which extends {@link android.accessibilityservice.AccessibilityService}.
+</p>
+</body>
+</html>
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 7e41d36..4f97066 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1950,6 +1950,7 @@
// mInitialViewState is set by didFirstLayout() and then reset in the
// next webkitDraw after passing the state to the UI thread.
private ViewState mInitialViewState = null;
+ private boolean mFirstLayoutForNonStandardLoad;
static class ViewState {
float mMinScale;
@@ -1977,6 +1978,7 @@
int mMinPrefWidth;
// only non-null if it is for the first picture set after the first layout
ViewState mViewState;
+ boolean mFirstLayoutForNonStandardLoad;
boolean mFocusSizeChanged;
}
@@ -2026,6 +2028,10 @@
draw.mViewState = mInitialViewState;
mInitialViewState = null;
}
+ if (mFirstLayoutForNonStandardLoad) {
+ draw.mFirstLayoutForNonStandardLoad = true;
+ mFirstLayoutForNonStandardLoad = false;
+ }
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
Message.obtain(mWebView.mPrivateHandler,
WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
@@ -2312,6 +2318,8 @@
// if mViewportWidth is 0, it means device-width, always update.
if (mViewportWidth != 0 && !updateViewState) {
+ // For non standard load, since updateViewState will be false.
+ mFirstLayoutForNonStandardLoad = true;
ViewState viewState = new ViewState();
viewState.mMinScale = mViewportMinimumScale / 100.0f;
viewState.mMaxScale = mViewportMaximumScale / 100.0f;
@@ -2471,9 +2479,10 @@
// called by JNI
private void restoreScale(float scale, float textWrapScale) {
if (mBrowserFrame.firstLayoutDone() == false) {
- mRestoredScale = scale;
+ final float defaultScale = mWebView.getDefaultZoomScale();
+ mRestoredScale = (scale <= 0.0) ? defaultScale : scale;
if (mSettings.getUseWideViewPort()) {
- mRestoredTextWrapScale = textWrapScale;
+ mRestoredTextWrapScale = (textWrapScale <= 0.0) ? defaultScale : textWrapScale;
}
}
}
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 6c6974b..7d43e94 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1024,6 +1024,11 @@
} else {
mInZoomOverview = !scaleHasDiff;
}
+ if (drawData.mFirstLayoutForNonStandardLoad && settings.getLoadWithOverviewMode()) {
+ // Set mInitialZoomOverview in case this is the first picture for non standard load,
+ // so next new picture could be forced into overview mode if it's true.
+ mInitialZoomOverview = mInZoomOverview;
+ }
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2a70ac8..939779f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -340,6 +340,16 @@
private WordIterator mWordIterator;
+ // The alignment to pass to Layout, or null if not resolved.
+ private Layout.Alignment mLayoutAlignment;
+
+ // The default value for mTextAlign.
+ private TextAlign mTextAlign = TextAlign.INHERIT;
+
+ private static enum TextAlign {
+ INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END;
+ }
+
/*
* Kick-start the font cache for the zygote process (to pay the cost of
* initializing freetype for our default font only once).
@@ -5532,6 +5542,73 @@
physicalWidth, false);
}
+ @Override
+ protected void resetLayoutDirectionResolution() {
+ super.resetLayoutDirectionResolution();
+
+ if (mLayoutAlignment != null &&
+ (mTextAlign == TextAlign.VIEW_START ||
+ mTextAlign == TextAlign.VIEW_END)) {
+ mLayoutAlignment = null;
+ }
+ }
+
+ private Layout.Alignment getLayoutAlignment() {
+ if (mLayoutAlignment == null) {
+ Layout.Alignment alignment;
+ TextAlign textAlign = mTextAlign;
+ switch (textAlign) {
+ case INHERIT:
+ // fall through to gravity temporarily
+ // intention is to inherit value through view hierarchy.
+ case GRAVITY:
+ switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.START:
+ alignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ case Gravity.END:
+ alignment = Layout.Alignment.ALIGN_OPPOSITE;
+ break;
+ case Gravity.LEFT:
+ alignment = Layout.Alignment.ALIGN_LEFT;
+ break;
+ case Gravity.RIGHT:
+ alignment = Layout.Alignment.ALIGN_RIGHT;
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ alignment = Layout.Alignment.ALIGN_CENTER;
+ break;
+ default:
+ alignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ }
+ break;
+ case TEXT_START:
+ alignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ case TEXT_END:
+ alignment = Layout.Alignment.ALIGN_OPPOSITE;
+ break;
+ case CENTER:
+ alignment = Layout.Alignment.ALIGN_CENTER;
+ break;
+ case VIEW_START:
+ alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
+ break;
+ case VIEW_END:
+ alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+ break;
+ default:
+ alignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ }
+ mLayoutAlignment = alignment;
+ }
+ return mLayoutAlignment;
+ }
+
/**
* The width passed in is now the desired layout width,
* not the full view width with padding.
@@ -5552,25 +5629,7 @@
hintWidth = 0;
}
- final int layoutDirection = getResolvedLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- Layout.Alignment alignment;
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- alignment = Layout.Alignment.ALIGN_CENTER;
- break;
-
- case Gravity.RIGHT:
- // Note, Layout resolves ALIGN_OPPOSITE to left or
- // right based on the paragraph direction.
- alignment = Layout.Alignment.ALIGN_OPPOSITE;
- break;
-
- default:
- alignment = Layout.Alignment.ALIGN_NORMAL;
- }
-
+ Layout.Alignment alignment = getLayoutAlignment();
boolean shouldEllipsize = mEllipsize != null && mInput == null;
if (mText instanceof Spannable) {
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 8d5df6f..519acf5 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -16,7 +16,6 @@
package com.android.internal.app;
-import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.SubMenuBuilder;
@@ -36,7 +35,6 @@
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.ActionMode;
@@ -580,6 +578,9 @@
mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
+ if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
+ mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+ }
}
/**
@@ -620,6 +621,7 @@
// Clear out the context mode views after the animation finishes
mContextView.closeMode();
+
mActionMode = null;
if (mWasHiddenBeforeMode) {
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index d710cfa..953328c 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -105,24 +105,19 @@
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int nonTabHeight = 0;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
+ if (mActionBarView == null) return;
- if (child == mTabContainer) continue;
-
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- nonTabHeight = Math.max(nonTabHeight,
- child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- }
+ final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
+ final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 :
+ mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
final int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode == MeasureSpec.AT_MOST) {
final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(),
- Math.min(nonTabHeight + mTabContainer.getMeasuredHeight(), maxHeight));
+ Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(),
+ maxHeight));
}
}
}
@@ -137,12 +132,14 @@
if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) {
// Not showing home, put tabs on top.
final int count = getChildCount();
- for (int i = 0; i < count; i++){
+ for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child == mTabContainer) continue;
- child.offsetTopAndBottom(tabHeight);
+ if (!mActionBarView.isCollapsed()) {
+ child.offsetTopAndBottom(tabHeight);
+ }
}
mTabContainer.layout(l, 0, r, tabHeight);
} else {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index fc43994..3e3eeab 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -287,7 +287,7 @@
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
}
- if (mMenuView != null) {
+ if (mMenuView != null && mMenuView.getParent() == this) {
availableWidth = measureChildView(mMenuView, availableWidth,
childSpecHeight, 0);
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 8eb046e..09bc1fc 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -117,6 +117,7 @@
private boolean mUserTitle;
private boolean mIncludeTabs;
private boolean mIsCollapsable;
+ private boolean mIsCollapsed;
private MenuBuilder mOptionsMenu;
@@ -692,6 +693,10 @@
mIsCollapsable = collapsable;
}
+ public boolean isCollapsed() {
+ return mIsCollapsed;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int childCount = getChildCount();
@@ -708,9 +713,11 @@
if (visibleChildren == 0) {
// No size for an empty action bar when collapsable.
setMeasuredDimension(0, 0);
+ mIsCollapsed = true;
return;
}
}
+ mIsCollapsed = false;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 5b4d7ab..2f7adf0 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget;
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
import android.app.ActionBar;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -22,6 +25,7 @@
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -35,6 +39,13 @@
int mMaxTabWidth;
+ protected Animator mVisibilityAnim;
+ protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+ private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+ private static final int FADE_DURATION = 200;
+
public ScrollingTabContainerView(Context context) {
super(context);
setHorizontalScrollBarEnabled(false);
@@ -76,6 +87,30 @@
}
}
+ public void animateToVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.cancel();
+ }
+ if (visibility == VISIBLE) {
+ if (getVisibility() != VISIBLE) {
+ setAlpha(0);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ } else {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ }
+
public void animateToTab(int position) {
final View tabView = mTabLayout.getChildAt(position);
if (mTabSelector != null) {
@@ -259,4 +294,38 @@
}
}
}
+
+ protected class VisibilityAnimListener implements Animator.AnimatorListener {
+ private boolean mCanceled = false;
+ private int mFinalVisibility;
+
+ public VisibilityAnimListener withFinalVisibility(int visibility) {
+ mFinalVisibility = visibility;
+ return this;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ mVisibilityAnim = animation;
+ mCanceled = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) return;
+
+ mVisibilityAnim = null;
+ setVisibility(mFinalVisibility);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 5b35104..04bb689 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -20,7 +20,7 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
-import android.animation.ObjectAnimator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -43,9 +43,9 @@
* A special widget containing a center and outer ring. Moving the center ring to the outer ring
* causes an event that can be caught by implementing OnTriggerListener.
*/
-public class MultiWaveView extends View implements AnimatorUpdateListener {
+public class MultiWaveView extends View {
private static final String TAG = "MultiWaveView";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
// Wave state machine
private static final int STATE_IDLE = 0;
@@ -67,14 +67,15 @@
}
// Tune-able parameters
- private static final int CHEVRON_INCREMENTAL_DELAY = 50;
- private static final int CHEVRON_ANIMATION_DURATION = 1000;
- private static final int RETURN_TO_HOME_DURATION = 150;
- private static final int HIDE_ANIMATION_DELAY = 500;
- private static final int HIDE_ANIMATION_DURACTION = 2000;
+ private static final int CHEVRON_INCREMENTAL_DELAY = 160;
+ private static final int CHEVRON_ANIMATION_DURATION = 650;
+ private static final int RETURN_TO_HOME_DELAY = 1200;
+ private static final int RETURN_TO_HOME_DURATION = 300;
+ private static final int HIDE_ANIMATION_DELAY = 200;
+ private static final int HIDE_ANIMATION_DURATION = RETURN_TO_HOME_DELAY;
private static final int SHOW_ANIMATION_DURATION = 0;
private static final int SHOW_ANIMATION_DELAY = 0;
- private TimeInterpolator mChevronAnimationInterpolator = Ease.Quint.easeOut;
+ private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>();
@@ -99,14 +100,31 @@
private float mHitRadius = 0.0f;
private float mSnapMargin = 0.0f;
private boolean mDragging;
+ private int mNewTargetResources;
- private AnimatorListener mResetListener = new Animator.AnimatorListener() {
- public void onAnimationStart(Animator animation) { }
- public void onAnimationRepeat(Animator animation) { }
- public void onAnimationEnd(Animator animation) {
+ private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animator) {
switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
}
- public void onAnimationCancel(Animator animation) { }
+ };
+
+ private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidateGlobalRegion(mHandleDrawable);
+ invalidate();
+ }
+ };
+
+ private boolean mAnimatingTargets;
+ private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animator) {
+ if (mNewTargetResources != 0) {
+ internalSetTargetResources(mNewTargetResources);
+ mNewTargetResources = 0;
+ hideTargets(false);
+ }
+ mAnimatingTargets = false;
+ }
};
public MultiWaveView(Context context) {
@@ -135,31 +153,23 @@
mOuterRing = new TargetDrawable(res, a.getDrawable(R.styleable.MultiWaveView_waveDrawable));
// Read chevron animation drawables
- Drawable leftChevron = a.getDrawable(R.styleable.MultiWaveView_leftChevronDrawable);
- for (int i = 0; i < mFeedbackCount; i++) {
- mChevronDrawables.add(
- leftChevron != null ? new TargetDrawable(res, leftChevron) : null);
- }
- Drawable rightChevron = a.getDrawable(R.styleable.MultiWaveView_rightChevronDrawable);
- for (int i = 0; i < mFeedbackCount; i++) {
- mChevronDrawables.add(
- rightChevron != null ? new TargetDrawable(res, rightChevron) : null);
- }
- Drawable topChevron = a.getDrawable(R.styleable.MultiWaveView_topChevronDrawable);
- for (int i = 0; i < mFeedbackCount; i++) {
- mChevronDrawables.add(
- topChevron != null ? new TargetDrawable(res, topChevron) : null);
- }
- Drawable bottomChevron = a.getDrawable(R.styleable.MultiWaveView_bottomChevronDrawable);
- for (int i = 0; i < mFeedbackCount; i++) {
- mChevronDrawables.add(
- bottomChevron != null ? new TargetDrawable(res, bottomChevron) : null);
+ final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable,
+ R.styleable.MultiWaveView_rightChevronDrawable,
+ R.styleable.MultiWaveView_topChevronDrawable,
+ R.styleable.MultiWaveView_bottomChevronDrawable
+ };
+ for (int chevron : chevrons) {
+ Drawable chevronDrawable = a.getDrawable(chevron);
+ for (int i = 0; i < mFeedbackCount; i++) {
+ mChevronDrawables.add(
+ chevronDrawable != null ? new TargetDrawable(res, chevronDrawable) : null);
+ }
}
// Read array of target drawables
TypedValue outValue = new TypedValue();
if (a.getValue(R.styleable.MultiWaveView_targetDrawables, outValue)) {
- setTargetResources(outValue.resourceId);
+ internalSetTargetResources(outValue.resourceId);
}
if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
throw new IllegalStateException("Must specify at least one target drawable");
@@ -205,7 +215,7 @@
case STATE_FIRST_TOUCH:
stopHandleAnimation();
deactivateTargets();
- showTargets();
+ showTargets(true);
mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
setGrabbedState(OnTriggerListener.CENTER_HANDLE);
break;
@@ -228,17 +238,18 @@
* mFeedbackCount items in the order: left, right, top, bottom.
*/
private void startChevronAnimation() {
- final float r = mHandleDrawable.getWidth() / 2;
+ final float r = mHandleDrawable.getWidth() * 0.4f;
+ final float chevronAnimationDistance = mOuterRadius * 0.8f;
final float from[][] = {
{mWaveCenterX - r, mWaveCenterY}, // left
{mWaveCenterX + r, mWaveCenterY}, // right
{mWaveCenterX, mWaveCenterY - r}, // top
{mWaveCenterX, mWaveCenterY + r} }; // bottom
final float to[][] = {
- {mWaveCenterX - mOuterRadius, mWaveCenterY}, // left
- {mWaveCenterX + mOuterRadius, mWaveCenterY}, // right
- {mWaveCenterX, mWaveCenterY - mOuterRadius}, // top
- {mWaveCenterX, mWaveCenterY + mOuterRadius} }; // bottom
+ {mWaveCenterX - chevronAnimationDistance, mWaveCenterY}, // left
+ {mWaveCenterX + chevronAnimationDistance, mWaveCenterY}, // right
+ {mWaveCenterX, mWaveCenterY - chevronAnimationDistance}, // top
+ {mWaveCenterX, mWaveCenterY + chevronAnimationDistance} }; // bottom
mChevronAnimations.clear();
for (int direction = 0; direction < 4; direction++) {
@@ -254,7 +265,7 @@
"x", new float[] { from[direction][0], to[direction][0] },
"y", new float[] { from[direction][1], to[direction][1] },
"alpha", new float[] {1.0f, 0.0f},
- "onUpdate", this));
+ "onUpdate", mUpdateListener));
}
}
}
@@ -308,70 +319,109 @@
}
private void doFinish() {
- // Inform listener of any active targets. Typically only one will be active.
final int activeTarget = mActiveTarget;
boolean targetHit = activeTarget != -1;
- if (targetHit) {
- Log.v(TAG, "Finish with target hit = " + targetHit);
- dispatchTriggerEvent(mActiveTarget);
- }
-
- setGrabbedState(OnTriggerListener.NO_HANDLE);
-
- // Animate finger outline back to home position
- mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f);
- mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION,
- "ease", Ease.Quart.easeOut,
- "delay", targetHit ? HIDE_ANIMATION_DELAY : 0,
- "alpha", 1.0f,
- "x", mWaveCenterX,
- "y", mWaveCenterY,
- "onUpdate", this,
- "onComplete", mResetListener);
// Hide unselected targets
hideTargets(true);
// Highlight the selected one
+ mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f);
if (targetHit) {
mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
+
+ hideUnselected(activeTarget);
+
+ // Inform listener of any active targets. Typically only one will be active.
+ if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
+ dispatchTriggerEvent(mActiveTarget);
+ mHandleAnimation = Tweener.to(mHandleDrawable, 0,
+ "ease", Ease.Quart.easeOut,
+ "delay", RETURN_TO_HOME_DELAY,
+ "alpha", 1.0f,
+ "x", mWaveCenterX,
+ "y", mWaveCenterY,
+ "onUpdate", mUpdateListener,
+ "onComplete", mResetListener);
+ } else {
+ // Animate finger outline back to home position
+ mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION,
+ "ease", Ease.Quart.easeOut,
+ "delay", 0,
+ "alpha", 1.0f,
+ "x", mWaveCenterX,
+ "y", mWaveCenterY,
+ "onUpdate", mUpdateListener,
+ "onComplete", mResetListener);
}
+
+ setGrabbedState(OnTriggerListener.NO_HANDLE);
+ }
+
+ private void hideUnselected(int active) {
+ for (int i = 0; i < mTargetDrawables.size(); i++) {
+ if (i != active) {
+ mTargetDrawables.get(i).setAlpha(0.0f);
+ }
+ }
+ mOuterRing.setAlpha(0.0f);
}
private void hideTargets(boolean animate) {
if (mTargetAnimations.size() > 0) {
stopTargetAnimation();
}
- for (TargetDrawable target : mTargetDrawables) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target,
- animate ? HIDE_ANIMATION_DURACTION : 0,
+ // Note: these animations should complete at the same time so that we can swap out
+ // the target assets asynchronously from the setTargetResources() call.
+ mAnimatingTargets = animate;
+ if (animate) {
+ final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
+ for (TargetDrawable target : mTargetDrawables) {
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ mTargetAnimations.add(Tweener.to(target, duration,
+ "alpha", 0.0f,
+ "delay", HIDE_ANIMATION_DELAY,
+ "onUpdate", mUpdateListener));
+ }
+ mTargetAnimations.add(Tweener.to(mOuterRing, duration,
"alpha", 0.0f,
"delay", HIDE_ANIMATION_DELAY,
- "onUpdate", this));
+ "onUpdate", mUpdateListener,
+ "onComplete", mTargetUpdateListener));
+ } else {
+ for (TargetDrawable target : mTargetDrawables) {
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ target.setAlpha(0.0f);
+ }
+ mOuterRing.setAlpha(0.0f);
}
- mTargetAnimations.add(Tweener.to(mOuterRing,
- animate ? HIDE_ANIMATION_DURACTION : 0,
- "alpha", 0.0f,
- "delay", HIDE_ANIMATION_DELAY,
- "onUpdate", this));
}
- private void showTargets() {
+ private void showTargets(boolean animate) {
if (mTargetAnimations.size() > 0) {
stopTargetAnimation();
}
- for (TargetDrawable target : mTargetDrawables) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION,
+ mAnimatingTargets = animate;
+ if (animate) {
+ for (TargetDrawable target : mTargetDrawables) {
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION,
+ "alpha", 1.0f,
+ "delay", SHOW_ANIMATION_DELAY,
+ "onUpdate", mUpdateListener));
+ }
+ mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION,
"alpha", 1.0f,
"delay", SHOW_ANIMATION_DELAY,
- "onUpdate", this));
+ "onUpdate", mUpdateListener,
+ "onComplete", mTargetUpdateListener));
+ } else {
+ for (TargetDrawable target : mTargetDrawables) {
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ target.setAlpha(1.0f);
+ }
+ mOuterRing.setAlpha(1.0f);
}
- mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION,
- "alpha", 1.0f,
- "delay", SHOW_ANIMATION_DELAY,
- "onUpdate", this));
}
private void stopTargetAnimation() {
@@ -387,12 +437,7 @@
}
}
- /**
- * Loads an array of drawables from the given resourceId.
- *
- * @param resourceId
- */
- public void setTargetResources(int resourceId) {
+ private void internalSetTargetResources(int resourceId) {
Resources res = getContext().getResources();
TypedArray array = res.obtainTypedArray(resourceId);
int count = array.length();
@@ -402,6 +447,21 @@
targetDrawables.add(new TargetDrawable(res, drawable));
}
mTargetDrawables = targetDrawables;
+ updateTargetPositions();
+ }
+
+ /**
+ * Loads an array of drawables from the given resourceId.
+ *
+ * @param resourceId
+ */
+ public void setTargetResources(int resourceId) {
+ if (mAnimatingTargets) {
+ // postpone this change until we return to the initial state
+ mNewTargetResources = resourceId;
+ } else {
+ internalSetTargetResources(resourceId);
+ }
}
/**
@@ -590,20 +650,9 @@
}
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- final int width = right - left;
- final int height = bottom - top;
-
- mWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2;
- mWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2;
- moveHandleTo(mWaveCenterX, mWaveCenterY, false);
- mOuterRing.setX(mWaveCenterX);
- mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY));
- mOuterRing.setAlpha(0.0f);
+ private void performInitialLayout(float centerX, float centerY) {
if (mOuterRadius == 0.0f) {
- mOuterRadius = 0.5f*(float) Math.sqrt(dist2(mWaveCenterX, mWaveCenterY));
+ mOuterRadius = 0.5f*(float) Math.sqrt(dist2(centerX, centerY));
}
if (mHitRadius == 0.0f) {
// Use the radius of inscribed circle of the first target.
@@ -613,6 +662,35 @@
mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
}
+ hideChevrons();
+ hideTargets(false);
+ moveHandleTo(centerX, centerY, false);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ final int width = right - left;
+ final int height = bottom - top;
+ float newWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2;
+ float newWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2;
+ if (newWaveCenterX != mWaveCenterX || newWaveCenterY != mWaveCenterY) {
+ if (mWaveCenterX == 0 && mWaveCenterY == 0) {
+ performInitialLayout(newWaveCenterX, newWaveCenterY);
+ }
+ mWaveCenterX = newWaveCenterX;
+ mWaveCenterY = newWaveCenterY;
+
+ mOuterRing.setX(mWaveCenterX);
+ mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY));
+
+ updateTargetPositions();
+ }
+ if (DEBUG) dump();
+ }
+
+ private void updateTargetPositions() {
+ // Reposition the target drawables if the view changed.
for (int i = 0; i < mTargetDrawables.size(); i++) {
final TargetDrawable targetIcon = mTargetDrawables.get(i);
double angle = -2.0f * Math.PI * i / mTargetDrawables.size();
@@ -620,11 +698,7 @@
float yPosition = mWaveCenterY + mOuterRadius * (float) Math.sin(angle);
targetIcon.setX(xPosition);
targetIcon.setY(yPosition);
- targetIcon.setAlpha(0.0f);
}
- hideChevrons();
- hideTargets(false);
- if (DEBUG) dump();
}
private void hideChevrons() {
@@ -655,11 +729,6 @@
mOnTriggerListener = listener;
}
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidateGlobalRegion(mHandleDrawable);
- invalidate();
- }
-
private float square(float d) {
return d * d;
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index fd61cfd..db33d1c 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2208,7 +2208,8 @@
<!-- The event types this serivce would like to receive as specified in
{@link android.view.accessibility.AccessibilityEvent}. This setting
can be changed at runtime by calling
- {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="accessibilityEventTypes">
<!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED} events.-->
<flag name="typeViewClicked" value="0x00000001" />
@@ -2232,17 +2233,24 @@
<flag name="typeTouchExplorationGestureStart" value="0x00000200" />
<!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} events. -->
<flag name="typeTouchExplorationGestureEnd" value="0x00000400" />
+ <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events. -->
+ <flag name="typeWindowContentChanged" value="0x00000800" />
+ <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED} events. -->
+ <flag name="typeViewScrolled" value="0x000001000" />
+ <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} events. -->
+ <flag name="typeViewTextSelectionChanged" value="0x000002000" />
<!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPES_ALL_MASK} i.e. all events. -->
<flag name="typeAllMask" value="0xffffffff" />
</attr>
<!-- Comma separated package names from which this serivce would like to receive events (leave out for all packages).
- This setting can be changed at runtime by calling
- {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="packageNames" format="string" />
<!-- The feedback types this serivce provides as specified in
{@link android.accessibilityservice.AccessibilityServiceInfo}. This setting
can be changed at runtime by calling
- {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="accessibilityFeedbackType">
<!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_SPOKEN} feedback. -->
<flag name="feedbackSpoken" value="0x00000001" />
@@ -2255,14 +2263,16 @@
<!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_GENERIC} feedback. -->
<flag name="feedbackGeneric" value="0x00000010" />
</attr>
- <!-- The minimal period in milliseconds between two accessibility events are sent
- to this serivce. This setting can be changed at runtime by calling
- {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+ <!-- The minimal period in milliseconds between two accessibility events of the same type
+ are sent to this serivce. This setting can be changed at runtime by calling
+ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->>
<attr name="notificationTimeout" format="integer" />
<!-- Additional flags as specified in
{@link android.accessibilityservice.AccessibilityServiceInfo}.
This setting can be changed at runtime by calling
- {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="accessibilityFlags">
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#DEFAULT} -->
<flag name="flagDefault" value="0x00000001" />
@@ -2271,7 +2281,7 @@
the settings for this service. This setting cannot be changed at runtime. -->
<attr name="settingsActivity" />
<!-- Flag whether the accessibility service wants to be able to retrieve the
- focused window content. This setting cannot be changed at runtime. -->
+ active window content. This setting cannot be changed at runtime. -->
<attr name="canRetrieveWindowContent" format="boolean" />
</declare-styleable>
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 513eda8..167071a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2282,6 +2282,11 @@
if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
CHECK(!mFlushComplete[data2]);
mFlushComplete[data2] = true;
+
+ if (mFlushComplete[kPortIndexInput]
+ && mFlushComplete[kPortIndexOutput]) {
+ changeStateIfWeOwnAllBuffers();
+ }
} else {
CHECK_EQ(data2, OMX_ALL);
CHECK(mFlushComplete[kPortIndexInput]);
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 165683e..f1a2a60 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -408,13 +408,20 @@
if (firstTime) {
Mutex::Autolock autoLock(mLock);
- int32_t targetDuration;
- if (!mPlaylist->isComplete()
- || !mPlaylist->meta()->findInt32(
- "target-duration", &targetDuration)) {
+ if (!mPlaylist->isComplete()) {
mDurationUs = -1;
} else {
- mDurationUs = 1000000ll * targetDuration * mPlaylist->size();
+ mDurationUs = 0;
+ for (size_t i = 0; i < mPlaylist->size(); ++i) {
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(
+ i, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ mDurationUs += itemDurationUs;
+ }
}
}
@@ -431,14 +438,26 @@
bool bandwidthChanged = false;
if (mSeekTimeUs >= 0) {
- int32_t targetDuration;
- if (mPlaylist->isComplete() &&
- mPlaylist->meta()->findInt32(
- "target-duration", &targetDuration)) {
- int64_t seekTimeSecs = (mSeekTimeUs + 500000ll) / 1000000ll;
- int64_t index = seekTimeSecs / targetDuration;
+ if (mPlaylist->isComplete()) {
+ size_t index = 0;
+ int64_t segmentStartUs = 0;
+ while (index < mPlaylist->size()) {
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(
+ index, NULL /* uri */, &itemMeta));
- if (index >= 0 && index < mPlaylist->size()) {
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ if (mSeekTimeUs < segmentStartUs + itemDurationUs) {
+ break;
+ }
+
+ segmentStartUs += itemDurationUs;
+ ++index;
+ }
+
+ if (index < mPlaylist->size()) {
int32_t newSeqNumber = firstSeqNumberInPlaylist + index;
if (newSeqNumber != mSeqNumber) {
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 765f795..123fbf8 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -64,14 +64,21 @@
}
bool M3UParser::itemAt(size_t index, AString *uri, sp<AMessage> *meta) {
- uri->clear();
- if (meta) { *meta = NULL; }
+ if (uri) {
+ uri->clear();
+ }
+
+ if (meta) {
+ *meta = NULL;
+ }
if (index >= mItems.size()) {
return false;
}
- *uri = mItems.itemAt(index).mURI;
+ if (uri) {
+ *uri = mItems.itemAt(index).mURI;
+ }
if (meta) {
*meta = mItems.itemAt(index).mMeta;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index fd0505e..f03f7a2 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -301,9 +301,6 @@
// static
bool ASessionDescription::parseNTPRange(
const char *s, float *npt1, float *npt2) {
- *npt1 = 0.0f;
- *npt2 = 0.0f;
-
if (s[0] == '-') {
return false; // no start time available.
}
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index f89f8e2..d15d9c5 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -995,12 +995,10 @@
AString val;
CHECK(GetAttribute(range.c_str(), "npt", &val));
- bool seekable = true;
-
float npt1, npt2;
if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) {
// This is a live stream and therefore not seekable.
- seekable = false;
+ return;
}
i = response->mHeaders.indexOfKey("rtp-info");
@@ -1046,7 +1044,7 @@
++n;
}
- mSeekable = seekable;
+ mSeekable = true;
}
sp<APacketSource> getPacketSource(size_t index) {
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 8b7a61e..1c4084c 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -64,7 +64,6 @@
private KeyguardUpdateMonitor mUpdateMonitor;
private KeyguardScreenCallback mCallback;
- private SlidingTab mSlidingTab;
private TextView mScreenLocked;
private TextView mEmergencyCallText;
private Button mEmergencyCallButton;
@@ -89,11 +88,9 @@
private boolean mEnableMenuKeyInLockScreen;
private StatusView mStatusView;
- private WaveView mEnergyWave;
- private SlidingTabMethods mSlidingTabMethods;
- private WaveViewMethods mWaveViewMethods;
- private MultiWaveView mMultiWaveView;
- private MultiWaveViewMethods mMultiWaveViewMethods;
+ private UnlockWidgetCommonMethods mUnlockWidgetMethods;
+ private View mUnlockWidget;
+
/**
* The status of this lock screen.
@@ -151,9 +148,28 @@
}
}
- class SlidingTabMethods implements SlidingTab.OnTriggerListener {
+ private interface UnlockWidgetCommonMethods {
+ // Update resources based on phone state
+ public void updateResources();
- private void updateRightTabResources() {
+ // Get the view associated with this widget
+ public View getView();
+
+ // Reset the view
+ public void reset(boolean animate);
+
+ // Animate the widget if it supports ping()
+ public void ping();
+ }
+
+ class SlidingTabMethods implements SlidingTab.OnTriggerListener, UnlockWidgetCommonMethods {
+ private final SlidingTab mSlidingTab;
+
+ SlidingTabMethods(SlidingTab slidingTab) {
+ mSlidingTab = slidingTab;
+ }
+
+ public void updateResources() {
boolean vibe = mSilentMode
&& (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
@@ -175,7 +191,6 @@
mCallback.goToUnlockScreen();
} else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
toggleRingMode();
- updateRightTabResources();
doSilenceRingToast();
mCallback.pokeWakelock();
}
@@ -195,12 +210,29 @@
mCallback.pokeWakelock();
}
}
+
+ public View getView() {
+ return mSlidingTab;
+ }
+
+ public void reset(boolean animate) {
+ mSlidingTab.reset(animate);
+ }
+
+ public void ping() {
+ }
}
private static final int WAIT_FOR_ANIMATION_TIMEOUT = 0;
private static final int STAY_ON_WHILE_GRABBED_TIMEOUT = 30000;
- class WaveViewMethods implements WaveView.OnTriggerListener {
+ class WaveViewMethods implements WaveView.OnTriggerListener, UnlockWidgetCommonMethods {
+
+ private final WaveView mWaveView;
+
+ WaveViewMethods(WaveView waveView) {
+ mWaveView = waveView;
+ }
/** {@inheritDoc} */
public void onTrigger(View v, int whichHandle) {
if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) {
@@ -210,8 +242,6 @@
/** {@inheritDoc} */
public void onGrabbedStateChange(View v, int grabbedState) {
- if (DBG) Log.v(TAG, "*** LockScreen accel is "
- + (mEnergyWave.isHardwareAccelerated() ? "on":"off"));
// Don't poke the wake lock when returning to a state where the handle is
// not grabbed since that can happen when the system (instead of the user)
// cancels the grab.
@@ -219,30 +249,51 @@
mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT);
}
}
+
+ public void updateResources() {
+ }
+
+ public View getView() {
+ return mWaveView;
+ }
+ public void reset(boolean animate) {
+ mWaveView.reset();
+ }
+ public void ping() {
+ }
}
- class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener {
+ class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener,
+ UnlockWidgetCommonMethods {
+
+ private final MultiWaveView mMultiWaveView;
+
+ MultiWaveViewMethods(MultiWaveView multiWaveView) {
+ mMultiWaveView = multiWaveView;
+ }
+
+ public void updateResources() {
+ mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent
+ : R.array.lockscreen_targets_when_soundon);
+ }
+
public void onGrabbed(View v, int handle) {
}
+
public void onReleased(View v, int handle) {
}
+
public void onTrigger(View v, int target) {
if (target == 0) { // TODO: Use resources to determine which handle was used
mCallback.goToUnlockScreen();
} else if (target == 2) {
toggleRingMode();
- updateResources();
doSilenceRingToast();
+ mUnlockWidgetMethods.updateResources();
mCallback.pokeWakelock();
}
-
- }
-
- private void updateResources() {
- mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent
- : R.array.lockscreen_targets_when_soundon);
}
public void onGrabbedStateChange(View v, int handle) {
@@ -253,6 +304,18 @@
mCallback.pokeWakelock();
}
}
+
+ public View getView() {
+ return mMultiWaveView;
+ }
+
+ public void reset(boolean animate) {
+ mMultiWaveView.reset(animate);
+ }
+
+ public void ping() {
+ mMultiWaveView.ping();
+ }
}
private void requestUnlockScreen() {
@@ -371,32 +434,39 @@
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mSilentMode = isSilentMode();
- View unlockWidget = findViewById(R.id.unlock_widget);
- if (unlockWidget instanceof SlidingTab) {
- mSlidingTab = (SlidingTab) unlockWidget;
- mSlidingTab.setHoldAfterTrigger(true, false);
- mSlidingTab.setLeftHintText(R.string.lockscreen_unlock_label);
- mSlidingTab.setLeftTabResources(
+ mUnlockWidget = findViewById(R.id.unlock_widget);
+ if (mUnlockWidget instanceof SlidingTab) {
+ SlidingTab slidingTabView = (SlidingTab) mUnlockWidget;
+ slidingTabView.setHoldAfterTrigger(true, false);
+ slidingTabView.setLeftHintText(R.string.lockscreen_unlock_label);
+ slidingTabView.setLeftTabResources(
R.drawable.ic_jog_dial_unlock,
R.drawable.jog_tab_target_green,
R.drawable.jog_tab_bar_left_unlock,
R.drawable.jog_tab_left_unlock);
- mSlidingTabMethods = new SlidingTabMethods();
- mSlidingTab.setOnTriggerListener(mSlidingTabMethods);
- mSlidingTabMethods.updateRightTabResources();
- } else if (unlockWidget instanceof WaveView) {
- mEnergyWave = (WaveView) unlockWidget;
- mWaveViewMethods = new WaveViewMethods();
- mEnergyWave.setOnTriggerListener(mWaveViewMethods);
- } else if (unlockWidget instanceof MultiWaveView) {
- mMultiWaveView = (MultiWaveView) unlockWidget;
- mMultiWaveViewMethods = new MultiWaveViewMethods();
- mMultiWaveViewMethods.updateResources(); // update silence/ring resources
- mMultiWaveView.setOnTriggerListener(mMultiWaveViewMethods);
+ SlidingTabMethods slidingTabMethods = new SlidingTabMethods(slidingTabView);
+ slidingTabView.setOnTriggerListener(slidingTabMethods);
+ mUnlockWidgetMethods = slidingTabMethods;
+ } else if (mUnlockWidget instanceof WaveView) {
+ WaveView waveView = (WaveView) mUnlockWidget;
+ WaveViewMethods waveViewMethods = new WaveViewMethods(waveView);
+ waveView.setOnTriggerListener(waveViewMethods);
+ mUnlockWidgetMethods = waveViewMethods;
+ } else if (mUnlockWidget instanceof MultiWaveView) {
+ MultiWaveView multiWaveView = (MultiWaveView) mUnlockWidget;
+ MultiWaveViewMethods multiWaveViewMethods = new MultiWaveViewMethods(multiWaveView);
+ multiWaveView.setOnTriggerListener(multiWaveViewMethods);
+ mUnlockWidgetMethods = multiWaveViewMethods;
} else {
- throw new IllegalStateException("Unrecognized unlock widget: " + unlockWidget);
+ throw new IllegalStateException("Unrecognized unlock widget: " + mUnlockWidget);
}
+ // Update widget with initial ring state
+ mUnlockWidgetMethods.updateResources();
+
+ if (DBG) Log.v(TAG, "*** LockScreen accel is "
+ + (mUnlockWidget.isHardwareAccelerated() ? "on":"off"));
+
resetStatusInfo(updateMonitor);
}
@@ -540,16 +610,14 @@
* Enables unlocking of this screen. Typically just shows the unlock widget.
*/
private void enableUnlock() {
- if (mEnergyWave != null) mEnergyWave.setVisibility(View.VISIBLE);
- if (mSlidingTab != null) mSlidingTab.setVisibility(View.VISIBLE);
+ mUnlockWidgetMethods.getView().setVisibility(View.VISIBLE);
}
/**
* Disable unlocking of this screen. Typically just hides the unlock widget.
*/
private void disableUnlock() {
- if (mEnergyWave != null) mEnergyWave.setVisibility(View.GONE);
- if (mSlidingTab != null) mSlidingTab.setVisibility(View.GONE);
+ mUnlockWidgetMethods.getView().setVisibility(View.GONE);
}
/**
@@ -728,20 +796,13 @@
/** {@inheritDoc} */
public void onPause() {
- if (mEnergyWave != null) {
- mEnergyWave.reset();
- }
- if (mMultiWaveView != null) {
- mMultiWaveView.reset(false);
- }
+ mUnlockWidgetMethods.reset(false);
}
/** {@inheritDoc} */
public void onResume() {
resetStatusInfo(mUpdateMonitor);
- if (mMultiWaveView != null) {
- mMultiWaveView.ping();
- }
+ mUnlockWidgetMethods.ping();
}
/** {@inheritDoc} */
@@ -757,7 +818,7 @@
boolean silent = AudioManager.RINGER_MODE_NORMAL != state;
if (silent != mSilentMode) {
mSilentMode = silent;
- if (mSlidingTabMethods != null) mSlidingTabMethods.updateRightTabResources();
+ mUnlockWidgetMethods.updateResources();
}
}
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 1d3e3ac..b3d7220 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -41,7 +41,7 @@
/**
* {@hide}
*/
-public class IntentResolver<F extends IntentFilter, R extends Object> {
+public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
final private static boolean localLOGV = DEBUG || false;
@@ -333,14 +333,19 @@
return false;
}
- protected String packageForFilter(F filter) {
- return null;
- }
+ /**
+ * Return the package that owns this filter. This must be implemented to
+ * provide correct filtering of Intents that have specified a package name
+ * they are to be delivered to.
+ */
+ protected abstract String packageForFilter(F filter);
+ @SuppressWarnings("unchecked")
protected R newResult(F filter, int match) {
return (R)filter;
}
+ @SuppressWarnings("unchecked")
protected void sortResults(List<R> results) {
Collections.sort(results, mResolvePrioritySorter);
}
@@ -502,6 +507,7 @@
String resolvedType, String scheme, List<F> src, List<R> dest) {
final String action = intent.getAction();
final Uri data = intent.getData();
+ final String packageName = intent.getPackage();
final boolean excludingStopped = intent.isExcludingStopped();
@@ -520,6 +526,14 @@
continue;
}
+ // Is delivery being limited to filters owned by a particular package?
+ if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (debug) {
+ Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
+ }
+ continue;
+ }
+
// Do we already have this one?
if (!allowFilterResult(filter, dest)) {
if (debug) {
@@ -561,6 +575,7 @@
}
// Sorts a List of IntentFilter objects into descending priority order.
+ @SuppressWarnings("rawtypes")
private static final Comparator mResolvePrioritySorter = new Comparator() {
public int compare(Object o1, Object o2) {
final int q1 = ((IntentFilter) o1).getPriority();
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 656ec4d..56afe7f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -195,6 +195,7 @@
final Object mKey;
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
int mPendingBroadcasts;
+ String requiredPermissions;
Receiver(ILocationListener listener) {
mListener = listener;
@@ -284,7 +285,8 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
+ mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+ requiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -319,7 +321,8 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
+ mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+ requiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -358,7 +361,8 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
+ mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+ requiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -572,22 +576,30 @@
return Settings.Secure.isLocationProviderEnabled(resolver, provider);
}
- private void checkPermissionsSafe(String provider) {
- if ((LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider))
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION permission");
+ private String checkPermissionsSafe(String provider, String lastPermission) {
+ if (LocationManager.GPS_PROVIDER.equals(provider)
+ || LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Provider " + provider
+ + " requires ACCESS_FINE_LOCATION permission");
+ }
+ return ACCESS_FINE_LOCATION;
}
- if (LocationManager.NETWORK_PROVIDER.equals(provider)
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)
- && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
+
+ // Assume any other provider requires the coarse or fine permission.
+ if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_FINE_LOCATION.equals(lastPermission)
+ ? lastPermission : ACCESS_COARSE_LOCATION;
}
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_FINE_LOCATION;
+ }
+
+ throw new SecurityException("Provider " + provider
+ + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
}
private boolean isAllowedProviderSafe(String provider) {
@@ -1099,8 +1111,21 @@
}
}
+ void validatePendingIntent(PendingIntent intent) {
+ if (intent.isTargetedToPackage()) {
+ return;
+ }
+ Slog.i(TAG, "Given Intent does not require a specific package: "
+ + intent);
+ // XXX we should really throw a security exception, if the caller's
+ // targetSdkVersion is high enough.
+ //throw new SecurityException("Given Intent does not require a specific package: "
+ // + intent);
+ }
+
public void requestLocationUpdatesPI(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
+ validatePendingIntent(intent);
if (criteria != null) {
// FIXME - should we consider using multiple providers simultaneously
// rather than only the best one?
@@ -1132,7 +1157,8 @@
throw new IllegalArgumentException("provider=" + provider);
}
- checkPermissionsSafe(provider);
+ receiver.requiredPermissions = checkPermissionsSafe(provider,
+ receiver.requiredPermissions);
// so wakelock calls will succeed
final int callingUid = Binder.getCallingUid();
@@ -1300,7 +1326,7 @@
}
// first check for permission to the provider
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1432,7 +1458,8 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcasts()
// is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
+ intent.send(mContext, 0, enteredIntent, this, mLocationHandler,
+ ACCESS_FINE_LOCATION);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcasts();
@@ -1457,7 +1484,8 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcasts()
// is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
+ intent.send(mContext, 0, exitedIntent, this, mLocationHandler,
+ ACCESS_FINE_LOCATION);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcasts();
@@ -1526,6 +1554,7 @@
public void addProximityAlert(double latitude, double longitude,
float radius, long expiration, PendingIntent intent) {
+ validatePendingIntent(intent);
try {
synchronized (mLock) {
addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
@@ -1626,7 +1655,7 @@
return null;
}
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
Bundle b = new Bundle();
b.putBoolean("network", p.requiresNetwork());
@@ -1668,7 +1697,7 @@
}
private boolean _isProviderEnabledLocked(String provider) {
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
@@ -1694,7 +1723,7 @@
}
private Location _getLastKnownLocationLocked(String provider) {
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4ec71c1..bf877f6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -622,6 +622,11 @@
}
return true;
}
+
+ @Override
+ protected String packageForFilter(BroadcastFilter filter) {
+ return filter.packageName;
+ }
};
/**
@@ -1825,6 +1830,8 @@
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
+ // If this is a new package in the process, add the package to the list
+ app.addPackage(info.packageName);
return app;
} else {
// An application record is attached to a previous process,
@@ -2278,7 +2285,7 @@
}
}
- return pir.sendInner(0, fillInIntent, resolvedType,
+ return pir.sendInner(0, fillInIntent, resolvedType, null,
null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
}
@@ -4162,6 +4169,27 @@
return null;
}
+ public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) {
+ if (!(pendingResult instanceof PendingIntentRecord)) {
+ return false;
+ }
+ try {
+ PendingIntentRecord res = (PendingIntentRecord)pendingResult;
+ if (res.key.allIntents == null) {
+ return false;
+ }
+ for (int i=0; i<res.key.allIntents.length; i++) {
+ Intent intent = res.key.allIntents[i];
+ if (intent.getPackage() != null && intent.getComponent() != null) {
+ return false;
+ }
+ }
+ return true;
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
public void setProcessLimit(int max) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessLimit()");
@@ -9895,6 +9923,7 @@
ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
if (app != null && app.thread != null) {
try {
+ app.addPackage(r.appInfo.packageName);
realStartServiceLocked(r, app);
return true;
} catch (RemoteException e) {
@@ -10945,7 +10974,7 @@
mBroadcastsScheduled = true;
}
- public Intent registerReceiver(IApplicationThread caller,
+ public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission) {
synchronized(this) {
ProcessRecord callerApp = null;
@@ -10957,6 +10986,13 @@
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
+ if (callerApp.info.uid != Process.SYSTEM_UID &&
+ !callerApp.pkgList.contains(callerPackage)) {
+ throw new SecurityException("Given caller package " + callerPackage
+ + " is not running in process " + callerApp);
+ }
+ } else {
+ callerPackage = null;
}
List allSticky = null;
@@ -11001,7 +11037,7 @@
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
- BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
+ BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -12155,6 +12191,7 @@
info.activityInfo.applicationInfo.uid);
if (app != null && app.thread != null) {
try {
+ app.addPackage(info.activityInfo.packageName);
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b94ee58..b1da69f 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -652,6 +652,7 @@
if (app != null && app.thread != null) {
try {
+ app.addPackage(r.info.packageName);
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index 2e784d3..b49bc22 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -25,12 +25,14 @@
class BroadcastFilter extends IntentFilter {
// Back-pointer to the list this filter is in.
final ReceiverList receiverList;
+ final String packageName;
final String requiredPermission;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _requiredPermission) {
+ String _packageName, String _requiredPermission) {
super(_filter);
receiverList = _receiverList;
+ packageName = _packageName;
requiredPermission = _requiredPermission;
}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index ee6e420..8ed0cc1 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -177,13 +177,13 @@
}
public int send(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver) {
+ IIntentReceiver finishedReceiver, String requiredPermission) {
return sendInner(code, intent, resolvedType, finishedReceiver,
- null, null, 0, 0, 0);
+ requiredPermission, null, null, 0, 0, 0);
}
int sendInner(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver,
+ IIntentReceiver finishedReceiver, String requiredPermission,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues) {
synchronized(owner) {
@@ -246,8 +246,8 @@
// that the broadcast be delivered synchronously
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
- finishedReceiver, code, null, null, null,
- (finishedReceiver != null), false);
+ finishedReceiver, code, null, null,
+ requiredPermission, (finishedReceiver != null), false);
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index f5efda9..54bddb2 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -90,7 +90,7 @@
// Reset the interface and hide the notification.
if (mInterfaceName != null) {
- nativeReset(mInterfaceName);
+ jniResetInterface(mInterfaceName);
mCallback.restore();
hideNotification();
mInterfaceName = null;
@@ -119,7 +119,7 @@
public void protect(ParcelFileDescriptor socket, String name) {
try {
mContext.enforceCallingPermission(VPN, "protect");
- nativeProtect(socket.getFd(), name);
+ jniProtectSocket(socket.getFd(), name);
} finally {
try {
socket.close();
@@ -152,17 +152,22 @@
}
// Create and configure the interface.
- ParcelFileDescriptor descriptor = ParcelFileDescriptor.adoptFd(
- nativeEstablish(config.mtu, config.addresses, config.routes));
+ ParcelFileDescriptor descriptor =
+ ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu));
- // Replace the interface and abort if it fails.
+ // Abort if any of the following steps fails.
try {
- String interfaceName = nativeGetName(descriptor.getFd());
-
- if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) {
- nativeReset(mInterfaceName);
+ String name = jniGetInterfaceName(descriptor.getFd());
+ if (jniSetAddresses(name, config.addresses) < 1) {
+ throw new IllegalArgumentException("At least one address must be specified");
}
- mInterfaceName = interfaceName;
+ if (config.routes != null) {
+ jniSetRoutes(name, config.routes);
+ }
+ if (mInterfaceName != null && !mInterfaceName.equals(name)) {
+ jniResetInterface(mInterfaceName);
+ }
+ mInterfaceName = name;
} catch (RuntimeException e) {
try {
descriptor.close();
@@ -195,7 +200,7 @@
// INetworkManagementEventObserver.Stub
public synchronized void interfaceRemoved(String name) {
- if (name.equals(mInterfaceName) && nativeCheck(name) == 0) {
+ if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) {
hideNotification();
mInterfaceName = null;
mCallback.restore();
@@ -253,11 +258,13 @@
}
}
- private native int nativeEstablish(int mtu, String addresses, String routes);
- private native String nativeGetName(int fd);
- private native void nativeReset(String name);
- private native int nativeCheck(String name);
- private native void nativeProtect(int fd, String name);
+ private native int jniCreateInterface(int mtu);
+ private native String jniGetInterfaceName(int fd);
+ private native int jniSetAddresses(String name, String addresses);
+ private native int jniSetRoutes(String name, String routes);
+ private native void jniResetInterface(String name);
+ private native int jniCheckInterface(String name);
+ private native void jniProtectSocket(int fd, String name);
/**
* Handle legacy VPN requests. This method stops the services and restart
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
index ae7fbfe..a0ea92b 100644
--- a/services/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -42,6 +42,9 @@
namespace android
{
+static int inet4 = -1;
+static int inet6 = -1;
+
static inline in_addr_t *as_in_addr(sockaddr *sa) {
return &((sockaddr_in *)sa)->sin_addr.s_addr;
}
@@ -51,11 +54,9 @@
#define SYSTEM_ERROR -1
#define BAD_ARGUMENT -2
-static int create_interface(int mtu, char *name, int *index)
+static int create_interface(int mtu)
{
- int tun = open("/dev/tun", O_RDWR);
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
- int flags;
+ int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
ifreq ifr4;
memset(&ifr4, 0, sizeof(ifr4));
@@ -81,38 +82,45 @@
goto error;
}
- // Get interface index.
- if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
- LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno));
- goto error;
- }
-
- // Make it non-blocking.
- flags = fcntl(tun, F_GETFL, 0);
- if (flags == -1 || fcntl(tun, F_SETFL, flags | O_NONBLOCK)) {
- LOGE("Cannot set non-blocking on %s: %s", ifr4.ifr_name, strerror(errno));
- goto error;
- }
-
- strcpy(name, ifr4.ifr_name);
- *index = ifr4.ifr_ifindex;
- close(inet4);
return tun;
error:
close(tun);
- close(inet4);
return SYSTEM_ERROR;
}
-static int set_addresses(const char *name, int index, const char *addresses)
+static int get_interface_name(char *name, int tun)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
- int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ ifreq ifr4;
+ if (ioctl(tun, TUNGETIFF, &ifr4)) {
+ LOGE("Cannot get interface name: %s", strerror(errno));
+ return SYSTEM_ERROR;
+ }
+ strncpy(name, ifr4.ifr_name, IFNAMSIZ);
+ return 0;
+}
+
+static int get_interface_index(const char *name)
+{
+ ifreq ifr4;
+ strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+ if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
+ LOGE("Cannot get index of %s: %s", name, strerror(errno));
+ return SYSTEM_ERROR;
+ }
+ return ifr4.ifr_ifindex;
+}
+
+static int set_addresses(const char *name, const char *addresses)
+{
+ int index = get_interface_index(name);
+ if (index < 0) {
+ return index;
+ }
ifreq ifr4;
memset(&ifr4, 0, sizeof(ifr4));
- strcpy(ifr4.ifr_name, name);
+ strncpy(ifr4.ifr_name, name, IFNAMSIZ);
ifr4.ifr_addr.sa_family = AF_INET;
in6_ifreq ifr6;
@@ -121,7 +129,6 @@
char address[65];
int prefix;
-
int chars;
int count = 0;
@@ -164,7 +171,7 @@
break;
}
}
- LOGV("Address added on %s: %s/%d", name, address, prefix);
+ LOGD("Address added on %s: %s/%d", name, address, prefix);
++count;
}
@@ -177,15 +184,15 @@
count = BAD_ARGUMENT;
}
- close(inet4);
- close(inet6);
return count;
}
-static int set_routes(const char *name, int index, const char *routes)
+static int set_routes(const char *name, const char *routes)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
- int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ int index = get_interface_index(name);
+ if (index < 0) {
+ return index;
+ }
rtentry rt4;
memset(&rt4, 0, sizeof(rt4));
@@ -201,7 +208,6 @@
char address[65];
int prefix;
-
int chars;
int count = 0;
@@ -211,32 +217,50 @@
if (strchr(address, ':')) {
// Add an IPv6 route.
if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
- prefix < 1 || prefix > 128) {
+ prefix < 0 || prefix > 128) {
count = BAD_ARGUMENT;
break;
}
- rt6.rtmsg_dst_len = prefix;
+ rt6.rtmsg_dst_len = prefix ? prefix : 1;
if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
break;
}
+
+ if (!prefix) {
+ // Split the route instead of replacing the default route.
+ rt6.rtmsg_dst.s6_addr[0] ^= 0x80;
+ if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
+ count = SYSTEM_ERROR;
+ break;
+ }
+ }
} else {
// Add an IPv4 route.
if (inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
- prefix < 1 || prefix > 32) {
+ prefix < 0 || prefix > 32) {
count = BAD_ARGUMENT;
break;
}
- in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+ in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 1;
*as_in_addr(&rt4.rt_genmask) = htonl(mask);
if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
break;
}
+
+ if (!prefix) {
+ // Split the route instead of replacing the default route.
+ *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000);
+ if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
+ count = SYSTEM_ERROR;
+ break;
+ }
+ }
}
- LOGV("Route added on %s: %s/%d", name, address, prefix);
+ LOGD("Route added on %s: %s/%d", name, address, prefix);
++count;
}
@@ -250,43 +274,24 @@
count = BAD_ARGUMENT;
}
- close(inet4);
- close(inet6);
return count;
}
-static int get_interface_name(char *name, int tun)
-{
- ifreq ifr4;
- if (ioctl(tun, TUNGETIFF, &ifr4)) {
- LOGE("Cannot get interface name: %s", strerror(errno));
- return SYSTEM_ERROR;
- }
- strcpy(name, ifr4.ifr_name);
- return 0;
-}
-
static int reset_interface(const char *name)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-
ifreq ifr4;
- ifr4.ifr_flags = 0;
strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+ ifr4.ifr_flags = 0;
if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
LOGE("Cannot reset %s: %s", name, strerror(errno));
- close(inet4);
return SYSTEM_ERROR;
}
- close(inet4);
return 0;
}
static int check_interface(const char *name)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-
ifreq ifr4;
strncpy(ifr4.ifr_name, name, IFNAMSIZ);
ifr4.ifr_flags = 0;
@@ -294,7 +299,6 @@
if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
LOGE("Cannot check %s: %s", name, strerror(errno));
}
- close(inet4);
return ifr4.ifr_flags;
}
@@ -318,86 +322,108 @@
}
}
-static jint establish(JNIEnv *env, jobject thiz,
- jint mtu, jstring jAddresses, jstring jRoutes)
+static jint createInterface(JNIEnv *env, jobject thiz, jint mtu)
{
- char name[IFNAMSIZ];
- int index;
- int tun = create_interface(mtu, name, &index);
+ int tun = create_interface(mtu);
if (tun < 0) {
throwException(env, tun, "Cannot create interface");
return -1;
}
- LOGD("%s is created", name);
-
- const char *addresses;
- const char *routes;
- int count;
-
- // Addresses are required.
- addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
- if (!addresses) {
- jniThrowNullPointerException(env, "address");
- goto error;
- }
- count = set_addresses(name, index, addresses);
- env->ReleaseStringUTFChars(jAddresses, addresses);
- if (count <= 0) {
- throwException(env, count, "Cannot set address");
- goto error;
- }
- LOGD("Configured %d address(es) on %s", count, name);
-
- // Routes are optional.
- routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
- if (routes) {
- count = set_routes(name, index, routes);
- env->ReleaseStringUTFChars(jRoutes, routes);
- if (count < 0) {
- throwException(env, count, "Cannot set route");
- goto error;
- }
- LOGD("Configured %d route(s) on %s", count, name);
- }
-
return tun;
-
-error:
- close(tun);
- LOGD("%s is destroyed", name);
- return -1;
}
-static jstring getName(JNIEnv *env, jobject thiz, jint fd)
+static jstring getInterfaceName(JNIEnv *env, jobject thiz, jint tun)
{
char name[IFNAMSIZ];
- if (get_interface_name(name, fd) < 0) {
+ if (get_interface_name(name, tun) < 0) {
throwException(env, SYSTEM_ERROR, "Cannot get interface name");
return NULL;
}
return env->NewStringUTF(name);
}
-static void reset(JNIEnv *env, jobject thiz, jstring jName)
+static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
+ jstring jAddresses)
{
- const char *name = jName ?
- env->GetStringUTFChars(jName, NULL) : NULL;
+ const char *name = NULL;
+ const char *addresses = NULL;
+ int count = -1;
+
+ name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
+ if (!name) {
+ jniThrowNullPointerException(env, "name");
+ goto error;
+ }
+ addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
+ if (!addresses) {
+ jniThrowNullPointerException(env, "addresses");
+ goto error;
+ }
+ count = set_addresses(name, addresses);
+ if (count < 0) {
+ throwException(env, count, "Cannot set address");
+ count = -1;
+ }
+
+error:
+ if (name) {
+ env->ReleaseStringUTFChars(jName, name);
+ }
+ if (addresses) {
+ env->ReleaseStringUTFChars(jAddresses, addresses);
+ }
+ return count;
+}
+
+static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
+ jstring jRoutes)
+{
+ const char *name = NULL;
+ const char *routes = NULL;
+ int count = -1;
+
+ name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
+ if (!name) {
+ jniThrowNullPointerException(env, "name");
+ goto error;
+ }
+ routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
+ if (!routes) {
+ jniThrowNullPointerException(env, "routes");
+ goto error;
+ }
+ count = set_routes(name, routes);
+ if (count < 0) {
+ throwException(env, count, "Cannot set address");
+ count = -1;
+ }
+
+error:
+ if (name) {
+ env->ReleaseStringUTFChars(jName, name);
+ }
+ if (routes) {
+ env->ReleaseStringUTFChars(jRoutes, routes);
+ }
+ return count;
+}
+
+static void resetInterface(JNIEnv *env, jobject thiz, jstring jName)
+{
+ const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return;
}
if (reset_interface(name) < 0) {
throwException(env, SYSTEM_ERROR, "Cannot reset interface");
- } else {
- LOGD("%s is deactivated", name);
}
env->ReleaseStringUTFChars(jName, name);
}
-static jint check(JNIEnv *env, jobject thiz, jstring jName)
+static jint checkInterface(JNIEnv *env, jobject thiz, jstring jName)
{
- const char *name = jName ?
- env->GetStringUTFChars(jName, NULL) : NULL;
+ const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return 0;
@@ -407,10 +433,9 @@
return flags;
}
-static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName)
+static void protectSocket(JNIEnv *env, jobject thiz, jint fd, jstring jName)
{
- const char *name = jName ?
- env->GetStringUTFChars(jName, NULL) : NULL;
+ const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return;
@@ -424,15 +449,23 @@
//------------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"nativeEstablish", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)establish},
- {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName},
- {"nativeReset", "(Ljava/lang/String;)V", (void *)reset},
- {"nativeCheck", "(Ljava/lang/String;)I", (void *)check},
- {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect},
+ {"jniCreateInterface", "(I)I", (void *)createInterface},
+ {"jniGetInterfaceName", "(I)Ljava/lang/String;", (void *)getInterfaceName},
+ {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
+ {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes},
+ {"jniResetInterface", "(Ljava/lang/String;)V", (void *)resetInterface},
+ {"jniCheckInterface", "(Ljava/lang/String;)I", (void *)checkInterface},
+ {"jniProtectSocket", "(ILjava/lang/String;)V", (void *)protectSocket},
};
int register_android_server_connectivity_Vpn(JNIEnv *env)
{
+ if (inet4 == -1) {
+ inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+ }
+ if (inet6 == -1) {
+ inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ }
return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
gMethods, NELEM(gMethods));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1c57bc1..685613e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1799,10 +1799,10 @@
const GLfloat h = hw_h - (hw_h * v);
const GLfloat x = (hw_w - w) * 0.5f;
const GLfloat y = (hw_h - h) * 0.5f;
- vtx[0] = x; vtx[1] = y;
- vtx[2] = x; vtx[3] = y + h;
- vtx[4] = x + w; vtx[5] = y + h;
- vtx[6] = x + w; vtx[7] = y;
+ vtx[0] = x; vtx[1] = y + h;
+ vtx[2] = x; vtx[3] = y;
+ vtx[4] = x + w; vtx[5] = y;
+ vtx[6] = x + w; vtx[7] = y + h;
}
};
@@ -1817,10 +1817,10 @@
const GLfloat h = 1.0f;
const GLfloat x = (hw_w - w) * 0.5f;
const GLfloat y = (hw_h - h) * 0.5f;
- vtx[0] = x; vtx[1] = y;
- vtx[2] = x; vtx[3] = y + h;
- vtx[4] = x + w; vtx[5] = y + h;
- vtx[6] = x + w; vtx[7] = y;
+ vtx[0] = x; vtx[1] = y + h;
+ vtx[2] = x; vtx[3] = y;
+ vtx[4] = x + w; vtx[5] = y;
+ vtx[6] = x + w; vtx[7] = y + h;
}
};