diff options
Diffstat (limited to 'core')
497 files changed, 20327 insertions, 26008 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 9d6ee80..811b92a 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -339,7 +339,10 @@ public abstract class AccessibilityService extends Service { private static final String LOG_TAG = "AccessibilityService"; - interface Callbacks { + /** + * @hide + */ + public interface Callbacks { public void onAccessibilityEvent(AccessibilityEvent event); public void onInterrupt(); public void onServiceConnected(); @@ -454,7 +457,7 @@ public abstract class AccessibilityService extends Service { * * @return The accessibility service info. * - * @see AccessibilityNodeInfo + * @see AccessibilityServiceInfo */ public final AccessibilityServiceInfo getServiceInfo() { IAccessibilityServiceConnection connection = @@ -538,8 +541,10 @@ public abstract class AccessibilityService extends Service { /** * Implements the internal {@link IAccessibilityServiceClient} interface to convert * incoming calls to it back to calls on an {@link AccessibilityService}. + * + * @hide */ - static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub + public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback { static final int NO_ID = -1; @@ -548,6 +553,7 @@ public abstract class AccessibilityService extends Service { private static final int DO_ON_INTERRUPT = 20; private static final int DO_ON_ACCESSIBILITY_EVENT = 30; private static final int DO_ON_GESTURE = 40; + private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50; private final HandlerCaller mCaller; @@ -580,6 +586,11 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } + public void clearAccessibilityNodeInfoCache() { + Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); + mCaller.sendMessage(message); + } + public void executeMessage(Message message) { switch (message.what) { case DO_ON_ACCESSIBILITY_EVENT : @@ -604,6 +615,7 @@ public abstract class AccessibilityService extends Service { mCallback.onServiceConnected(); } else { AccessibilityInteractionClient.getInstance().removeConnection(connectionId); + AccessibilityInteractionClient.getInstance().clearCache(); mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID); } return; @@ -611,6 +623,9 @@ public abstract class AccessibilityService extends Service { final int gestureId = message.arg1; mCallback.onGesture(gestureId); return; + case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: + AccessibilityInteractionClient.getInstance().clearCache(); + return; default : Log.w(LOG_TAG, "Unknown message type " + message.what); } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 75a4f83..d82b9a3 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -33,6 +33,7 @@ import android.util.TypedValue; import android.util.Xml; import android.view.View; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -148,8 +149,47 @@ public class AccessibilityServiceInfo implements Parcelable { * accessibility service that has this flag set. Hence, clearing this * flag does not guarantee that the device will not be in touch exploration * mode since there may be another enabled service that requested it. + * <p> + * For accessibility services targeting API version higher than + * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set + * this flag have to request the + * {@link android.Manifest.permission#CAN_REQUEST_TOUCH_EXPLORATION_MODE} + * permission or the flag will be ignored. + * </p> + * <p> + * Services targeting API version equal to or lower than + * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e. + * the first time they are run, if this flag is specified, a dialog is + * shown to the user to confirm enabling explore by touch. + * </p> + */ + public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004; + + /** + * This flag requests from the system to enable web accessibility enhancing + * extensions. Such extensions aim to provide improved accessibility support + * for content presented in a {@link android.webkit.WebView}. An example of such + * an extension is injecting JavaScript from a secure source. The system will enable + * enhanced web accessibility if there is at least one accessibility service + * that has this flag set. Hence, clearing this flag does not guarantee that the + * device will not have enhanced web accessibility enabled since there may be + * another enabled service that requested it. + * <p> + * Clients that want to set this flag have to request the + * {@link android.Manifest.permission#CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY} + * permission or the flag will be ignored. + * </p> */ - public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE= 0x0000004; + public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008; + + /** + * This flag requests that the {@link AccessibilityNodeInfo}s obtained + * by an {@link AccessibilityService} contain the id of the source view. + * The source view id will be a fully qualified resource name of the + * form "package:id/name", for example "foo.bar:id/my_list", and it is + * useful for UI test automation. This flag is not set by default. + */ + public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; /** * The event types an {@link AccessibilityService} is interested in. @@ -359,6 +399,13 @@ public class AccessibilityServiceInfo implements Parcelable { } /** + * @hide + */ + public void setComponentName(ComponentName component) { + mId = component.flattenToShortString(); + } + + /** * The accessibility service id. * <p> * <strong>Generated by the system.</strong> @@ -475,6 +522,33 @@ public class AccessibilityServiceInfo implements Parcelable { } @Override + public int hashCode() { + return 31 * 1 + ((mId == null) ? 0 : mId.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj; + if (mId == null) { + if (other.mId != null) { + return false; + } + } else if (!mId.equals(other.mId)) { + return false; + } + return true; + } + + @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); appendEventTypes(stringBuilder, eventTypes); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index d459fd5..5d684e3 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -33,4 +33,6 @@ import android.view.accessibility.AccessibilityEvent; void onInterrupt(); void onGesture(int gesture); + + void clearAccessibilityNodeInfoCache(); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index dd50f3c..7a29f35 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.os.Bundle; import android.accessibilityservice.AccessibilityServiceInfo; +import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; @@ -44,9 +45,9 @@ interface IAccessibilityServiceConnection { * @param callback Callback which to receive the result. * @param flags Additional flags. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId); @@ -66,9 +67,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, + boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); @@ -84,15 +85,15 @@ interface IAccessibilityServiceConnection { * where to start the search. Use * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. - * @param id The id of the node. + * @param viewId The fully qualified resource name of the view id to find. * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId, - int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, - long threadId); + boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, + long accessibilityNodeId, String viewId, int interactionId, + IAccessibilityInteractionConnectionCallback callback, long threadId); /** * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the specified @@ -110,9 +111,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, + boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); /** @@ -131,9 +132,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, + boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); /** diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java deleted file mode 100644 index 6837386..0000000 --- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accessibilityservice; - -import android.accessibilityservice.AccessibilityService.Callbacks; -import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper; -import android.content.Context; -import android.os.Bundle; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.util.Log; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityInteractionClient; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.IAccessibilityManager; - -import com.android.internal.util.Predicate; - -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class represents a bridge that can be used for UI test - * automation. It is responsible for connecting to the system, - * keeping track of the last accessibility event, and exposing - * window content querying APIs. This class is designed to be - * used from both an Android application and a Java program - * run from the shell. - * - * @hide - */ -public class UiTestAutomationBridge { - - private static final String LOG_TAG = UiTestAutomationBridge.class.getSimpleName(); - - private static final int TIMEOUT_REGISTER_SERVICE = 5000; - - public static final int ACTIVE_WINDOW_ID = AccessibilityNodeInfo.ACTIVE_WINDOW_ID; - - public static final long ROOT_NODE_ID = AccessibilityNodeInfo.ROOT_NODE_ID; - - public static final int UNDEFINED = -1; - - private static final int FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS = - AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS - | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS - | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS; - - private final Object mLock = new Object(); - - private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID; - - private IAccessibilityServiceClientWrapper mListener; - - private AccessibilityEvent mLastEvent; - - private volatile boolean mWaitingForEventDelivery; - - private volatile boolean mUnprocessedEventAvailable; - - private HandlerThread mHandlerThread; - - /** - * Gets the last received {@link AccessibilityEvent}. - * - * @return The event. - */ - public AccessibilityEvent getLastAccessibilityEvent() { - return mLastEvent; - } - - /** - * Callback for receiving an {@link AccessibilityEvent}. - * - * <strong>Note:</strong> This method is <strong>NOT</strong> - * executed on the application main thread. The client are - * responsible for proper synchronization. - * - * @param event The received event. - */ - public void onAccessibilityEvent(AccessibilityEvent event) { - /* hook - do nothing */ - } - - /** - * Callback for requests to stop feedback. - * - * <strong>Note:</strong> This method is <strong>NOT</strong> - * executed on the application main thread. The client are - * responsible for proper synchronization. - */ - public void onInterrupt() { - /* hook - do nothing */ - } - - /** - * Connects this service. - * - * @throws IllegalStateException If already connected. - */ - public void connect() { - if (isConnected()) { - throw new IllegalStateException("Already connected."); - } - - // Serialize binder calls to a handler on a dedicated thread - // different from the main since we expose APIs that block - // the main thread waiting for a result the deliver of which - // on the main thread will prevent that thread from waking up. - // The serialization is needed also to ensure that events are - // examined in delivery order. Otherwise, a fair locking - // is needed for making sure the binder calls are interleaved - // with check for the expected event and also to make sure the - // binder threads are allowed to proceed in the received order. - mHandlerThread = new HandlerThread("UiTestAutomationBridge"); - mHandlerThread.setDaemon(true); - mHandlerThread.start(); - Looper looper = mHandlerThread.getLooper(); - - mListener = new IAccessibilityServiceClientWrapper(null, looper, new Callbacks() { - @Override - public void onServiceConnected() { - /* do nothing */ - } - - @Override - public void onInterrupt() { - UiTestAutomationBridge.this.onInterrupt(); - } - - @Override - public void onAccessibilityEvent(AccessibilityEvent event) { - synchronized (mLock) { - while (true) { - mLastEvent = AccessibilityEvent.obtain(event); - if (!mWaitingForEventDelivery) { - mLock.notifyAll(); - break; - } - if (!mUnprocessedEventAvailable) { - mUnprocessedEventAvailable = true; - mLock.notifyAll(); - break; - } - try { - mLock.wait(); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } - UiTestAutomationBridge.this.onAccessibilityEvent(event); - } - - @Override - public void onSetConnectionId(int connectionId) { - synchronized (mLock) { - mConnectionId = connectionId; - mLock.notifyAll(); - } - } - - @Override - public boolean onGesture(int gestureId) { - return false; - } - }); - - final IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( - ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); - - final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); - info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; - info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; - info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; - - try { - manager.registerUiTestAutomationService(mListener, info); - } catch (RemoteException re) { - throw new IllegalStateException("Cound not register UiAutomationService.", re); - } - - synchronized (mLock) { - final long startTimeMillis = SystemClock.uptimeMillis(); - while (true) { - if (isConnected()) { - return; - } - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - final long remainingTimeMillis = TIMEOUT_REGISTER_SERVICE - elapsedTimeMillis; - if (remainingTimeMillis <= 0) { - throw new IllegalStateException("Cound not register UiAutomationService."); - } - try { - mLock.wait(remainingTimeMillis); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } - } - - /** - * Disconnects this service. - * - * @throws IllegalStateException If already disconnected. - */ - public void disconnect() { - if (!isConnected()) { - throw new IllegalStateException("Already disconnected."); - } - - mHandlerThread.quit(); - - IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( - ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); - - try { - manager.unregisterUiTestAutomationService(mListener); - } catch (RemoteException re) { - Log.e(LOG_TAG, "Error while unregistering UiTestAutomationService", re); - } - } - - /** - * Gets whether this service is connected. - * - * @return True if connected. - */ - public boolean isConnected() { - return (mConnectionId != AccessibilityInteractionClient.NO_ID); - } - - /** - * Executes a command and waits for a specific accessibility event type up - * to a given timeout. - * - * @param command The command to execute before starting to wait for the event. - * @param predicate Predicate for recognizing the awaited event. - * @param timeoutMillis The max wait time in milliseconds. - */ - public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command, - Predicate<AccessibilityEvent> predicate, long timeoutMillis) - throws TimeoutException, Exception { - // TODO: This is broken - remove from here when finalizing this as public APIs. - synchronized (mLock) { - // Prepare to wait for an event. - mWaitingForEventDelivery = true; - mUnprocessedEventAvailable = false; - if (mLastEvent != null) { - mLastEvent.recycle(); - mLastEvent = null; - } - // Execute the command. - command.run(); - // Wait for the event. - final long startTimeMillis = SystemClock.uptimeMillis(); - while (true) { - // If the expected event is received, that's it. - if ((mUnprocessedEventAvailable && predicate.apply(mLastEvent))) { - mWaitingForEventDelivery = false; - mUnprocessedEventAvailable = false; - mLock.notifyAll(); - return mLastEvent; - } - // Ask for another event. - mWaitingForEventDelivery = true; - mUnprocessedEventAvailable = false; - mLock.notifyAll(); - // Check if timed out and if not wait. - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; - if (remainingTimeMillis <= 0) { - mWaitingForEventDelivery = false; - mUnprocessedEventAvailable = false; - mLock.notifyAll(); - throw new TimeoutException("Expacted event not received within: " - + timeoutMillis + " ms."); - } - try { - mLock.wait(remainingTimeMillis); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } - } - - /** - * Waits for the accessibility event stream to become idle, which is not to - * have received a new accessibility event within <code>idleTimeout</code>, - * and do so within a maximal global timeout as specified by - * <code>globalTimeout</code>. - * - * @param idleTimeout The timeout between two event to consider the device idle. - * @param globalTimeout The maximal global timeout in which to wait for idle. - */ - public void waitForIdle(long idleTimeout, long globalTimeout) { - final long startTimeMillis = SystemClock.uptimeMillis(); - long lastEventTime = (mLastEvent != null) - ? mLastEvent.getEventTime() : SystemClock.uptimeMillis(); - synchronized (mLock) { - while (true) { - final long currentTimeMillis = SystemClock.uptimeMillis(); - final long sinceLastEventTimeMillis = currentTimeMillis - lastEventTime; - if (sinceLastEventTimeMillis > idleTimeout) { - return; - } - if (mLastEvent != null) { - lastEventTime = mLastEvent.getEventTime(); - } - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - final long remainingTimeMillis = globalTimeout - elapsedTimeMillis; - if (remainingTimeMillis <= 0) { - return; - } - try { - mLock.wait(idleTimeout); - } catch (InterruptedException e) { - /* ignore */ - } - } - } - } - - /** - * Finds an {@link AccessibilityNodeInfo} by accessibility id in the active - * window. The search is performed from the root node. - * - * @param accessibilityNodeId A unique view id or virtual descendant id for - * which to search. - * @return The current window scale, where zero means a failure. - */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityIdInActiveWindow( - long accessibilityNodeId) { - return findAccessibilityNodeInfoByAccessibilityId(ACTIVE_WINDOW_ID, accessibilityNodeId); - } - - /** - * Finds an {@link AccessibilityNodeInfo} by accessibility id. - * - * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} to query - * the currently active window. - * @param accessibilityNodeId A unique view id or virtual descendant id for - * which to search. - * @return The current window scale, where zero means a failure. - */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId( - int accessibilityWindowId, long accessibilityNodeId) { - // Cache the id to avoid locking - final int connectionId = mConnectionId; - ensureValidConnection(connectionId); - return AccessibilityInteractionClient.getInstance() - .findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - accessibilityWindowId, accessibilityNodeId, - FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS); - } - - /** - * Finds an {@link AccessibilityNodeInfo} by View id in the active - * window. The search is performed from the root node. - * - * @param viewId The id of a View. - * @return The current window scale, where zero means a failure. - */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) { - return findAccessibilityNodeInfoByViewId(ACTIVE_WINDOW_ID, ROOT_NODE_ID, viewId); - } - - /** - * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in - * the window whose id is specified and starts from the node whose accessibility - * id is specified. - * - * @param accessibilityWindowId A unique window id. Use - * {@link #ACTIVE_WINDOW_ID} to query the currently active window. - * @param accessibilityNodeId A unique view id or virtual descendant id from - * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. - * @param viewId The id of a View. - * @return The current window scale, where zero means a failure. - */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int accessibilityWindowId, - long accessibilityNodeId, int viewId) { - // Cache the id to avoid locking - final int connectionId = mConnectionId; - ensureValidConnection(connectionId); - return AccessibilityInteractionClient.getInstance() - .findAccessibilityNodeInfoByViewId(connectionId, accessibilityWindowId, - accessibilityNodeId, viewId); - } - - /** - * Finds {@link AccessibilityNodeInfo}s by View text in the active - * window. The search is performed from the root node. - * - * @param text The searched text. - * @return The current window scale, where zero means a failure. - */ - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByTextInActiveWindow(String text) { - return findAccessibilityNodeInfosByText(ACTIVE_WINDOW_ID, ROOT_NODE_ID, text); - } - - /** - * Finds {@link AccessibilityNodeInfo}s by View text. The match is case - * insensitive containment. The search is performed in the window whose - * id is specified and starts from the node whose accessibility id is - * specified. - * - * @param accessibilityWindowId A unique window id. Use - * {@link #ACTIVE_WINDOW_ID} to query the currently active window. - * @param accessibilityNodeId A unique view id or virtual descendant id from - * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. - * @param text The searched text. - * @return The current window scale, where zero means a failure. - */ - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int accessibilityWindowId, - long accessibilityNodeId, String text) { - // Cache the id to avoid locking - final int connectionId = mConnectionId; - ensureValidConnection(connectionId); - return AccessibilityInteractionClient.getInstance() - .findAccessibilityNodeInfosByText(connectionId, accessibilityWindowId, - accessibilityNodeId, text); - } - - /** - * Performs an accessibility action on an {@link AccessibilityNodeInfo} - * in the active window. - * - * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). - * @param action The action to perform. - * @param arguments Optional action arguments. - * @return Whether the action was performed. - */ - public boolean performAccessibilityActionInActiveWindow(long accessibilityNodeId, int action, - Bundle arguments) { - return performAccessibilityAction(ACTIVE_WINDOW_ID, accessibilityNodeId, action, arguments); - } - - /** - * Performs an accessibility action on an {@link AccessibilityNodeInfo}. - * - * @param accessibilityWindowId A unique window id. Use - * {@link #ACTIVE_WINDOW_ID} to query the currently active window. - * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). - * @param action The action to perform. - * @param arguments Optional action arguments. - * @return Whether the action was performed. - */ - public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, - int action, Bundle arguments) { - // Cache the id to avoid locking - final int connectionId = mConnectionId; - ensureValidConnection(connectionId); - return AccessibilityInteractionClient.getInstance().performAccessibilityAction(connectionId, - accessibilityWindowId, accessibilityNodeId, action, arguments); - } - - /** - * Gets the root {@link AccessibilityNodeInfo} in the active window. - * - * @return The root info. - */ - public AccessibilityNodeInfo getRootAccessibilityNodeInfoInActiveWindow() { - // Cache the id to avoid locking - final int connectionId = mConnectionId; - ensureValidConnection(connectionId); - return AccessibilityInteractionClient.getInstance() - .findAccessibilityNodeInfoByAccessibilityId(connectionId, ACTIVE_WINDOW_ID, - ROOT_NODE_ID, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); - } - - private void ensureValidConnection(int connectionId) { - if (connectionId == UNDEFINED) { - throw new IllegalStateException("UiAutomationService not connected." - + " Did you call #register()?"); - } - } -} diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java deleted file mode 100644 index f937cde..0000000 --- a/core/java/android/accounts/AccountAuthenticatorCache.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.XmlSerializerAndParser; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.text.TextUtils; -import android.util.AttributeSet; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; - -/** - * A cache of services that export the {@link IAccountAuthenticator} interface. This cache - * is built by interrogating the {@link PackageManager} and is updated as packages are added, - * removed and changed. The authenticators are referred to by their account type and - * are made available via the {@link RegisteredServicesCache#getServiceInfo} method. - * @hide - */ -/* package private */ class AccountAuthenticatorCache - extends RegisteredServicesCache<AuthenticatorDescription> - implements IAccountAuthenticatorCache { - private static final String TAG = "Account"; - private static final MySerializer sSerializer = new MySerializer(); - - public AccountAuthenticatorCache(Context context) { - super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT, - AccountManager.AUTHENTICATOR_META_DATA_NAME, - AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer); - } - - public AuthenticatorDescription parseServiceAttributes(Resources res, - String packageName, AttributeSet attrs) { - TypedArray sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AccountAuthenticator); - try { - final String accountType = - sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType); - final int labelId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_label, 0); - final int iconId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_icon, 0); - final int smallIconId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0); - final int prefId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0); - final boolean customTokens = sa.getBoolean( - com.android.internal.R.styleable.AccountAuthenticator_customTokens, false); - if (TextUtils.isEmpty(accountType)) { - return null; - } - return new AuthenticatorDescription(accountType, packageName, labelId, iconId, - smallIconId, prefId, customTokens); - } finally { - sa.recycle(); - } - } - - private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> { - public void writeAsXml(AuthenticatorDescription item, XmlSerializer out) - throws IOException { - out.attribute(null, "type", item.type); - } - - public AuthenticatorDescription createFromXml(XmlPullParser parser) - throws IOException, XmlPullParserException { - return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type")); - } - } -} diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java index 614e139..41f26ac 100644 --- a/core/java/android/accounts/AccountAuthenticatorResponse.java +++ b/core/java/android/accounts/AccountAuthenticatorResponse.java @@ -33,7 +33,7 @@ public class AccountAuthenticatorResponse implements Parcelable { /** * @hide */ - /* package private */ AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) { + public AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) { mAccountAuthenticatorResponse = response; } diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index bcb35d5..6d9bb1d 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -1846,7 +1846,7 @@ public class AccountManager { * Returns an intent to an {@link Activity} that prompts the user to choose from a list of * accounts. * The caller will then typically start the activity by calling - * <code>startActivityWithResult(intent, ...);</code>. + * <code>startActivityForResult(intent, ...);</code>. * <p> * On success the activity returns a Bundle with the account name and type specified using * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}. diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java deleted file mode 100644 index 2b1a2b2..0000000 --- a/core/java/android/accounts/AccountManagerService.java +++ /dev/null @@ -1,2548 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.Manifest; -import android.app.ActivityManager; -import android.app.ActivityManagerNative; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCacheListener; -import android.content.pm.UserInfo; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.os.Binder; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.UserManager; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.internal.R; -import com.android.internal.util.IndentingPrintWriter; -import com.google.android.collect.Lists; -import com.google.android.collect.Sets; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -/** - * A system service that provides account, password, and authtoken management for all - * accounts on the device. Some of these calls are implemented with the help of the corresponding - * {@link IAccountAuthenticator} services. This service is not accessed by users directly, - * instead one uses an instance of {@link AccountManager}, which can be accessed as follows: - * AccountManager accountManager = AccountManager.get(context); - * @hide - */ -public class AccountManagerService - extends IAccountManager.Stub - implements RegisteredServicesCacheListener<AuthenticatorDescription> { - private static final String TAG = "AccountManagerService"; - - private static final int TIMEOUT_DELAY_MS = 1000 * 60; - private static final String DATABASE_NAME = "accounts.db"; - private static final int DATABASE_VERSION = 4; - - private final Context mContext; - - private final PackageManager mPackageManager; - private UserManager mUserManager; - - private HandlerThread mMessageThread; - private final MessageHandler mMessageHandler; - - // Messages that can be sent on mHandler - private static final int MESSAGE_TIMED_OUT = 3; - - private final IAccountAuthenticatorCache mAuthenticatorCache; - - private static final String TABLE_ACCOUNTS = "accounts"; - private static final String ACCOUNTS_ID = "_id"; - private static final String ACCOUNTS_NAME = "name"; - private static final String ACCOUNTS_TYPE = "type"; - private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; - private static final String ACCOUNTS_PASSWORD = "password"; - - private static final String TABLE_AUTHTOKENS = "authtokens"; - private static final String AUTHTOKENS_ID = "_id"; - private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; - private static final String AUTHTOKENS_TYPE = "type"; - private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; - - private static final String TABLE_GRANTS = "grants"; - private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; - private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; - private static final String GRANTS_GRANTEE_UID = "uid"; - - private static final String TABLE_EXTRAS = "extras"; - private static final String EXTRAS_ID = "_id"; - private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; - private static final String EXTRAS_KEY = "key"; - private static final String EXTRAS_VALUE = "value"; - - private static final String TABLE_META = "meta"; - private static final String META_KEY = "key"; - private static final String META_VALUE = "value"; - - private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = - new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; - private static final Intent ACCOUNTS_CHANGED_INTENT; - - private static final String COUNT_OF_MATCHING_GRANTS = "" - + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS - + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID - + " AND " + GRANTS_GRANTEE_UID + "=?" - + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" - + " AND " + ACCOUNTS_NAME + "=?" - + " AND " + ACCOUNTS_TYPE + "=?"; - - private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = - AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; - private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, - AUTHTOKENS_AUTHTOKEN}; - - private static final String SELECTION_USERDATA_BY_ACCOUNT = - EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; - private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; - - private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); - private final AtomicInteger mNotificationIds = new AtomicInteger(1); - - static class UserAccounts { - private final int userId; - private final DatabaseHelper openHelper; - private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> - credentialsPermissionNotificationIds = - new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); - private final HashMap<Account, Integer> signinRequiredNotificationIds = - new HashMap<Account, Integer>(); - private final Object cacheLock = new Object(); - /** protected by the {@link #cacheLock} */ - private final HashMap<String, Account[]> accountCache = - new LinkedHashMap<String, Account[]>(); - /** protected by the {@link #cacheLock} */ - private HashMap<Account, HashMap<String, String>> userDataCache = - new HashMap<Account, HashMap<String, String>>(); - /** protected by the {@link #cacheLock} */ - private HashMap<Account, HashMap<String, String>> authTokenCache = - new HashMap<Account, HashMap<String, String>>(); - - UserAccounts(Context context, int userId) { - this.userId = userId; - synchronized (cacheLock) { - openHelper = new DatabaseHelper(context, userId); - } - } - } - - private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>(); - - private static AtomicReference<AccountManagerService> sThis = - new AtomicReference<AccountManagerService>(); - private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; - - static { - ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); - ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - } - - - /** - * This should only be called by system code. One should only call this after the service - * has started. - * @return a reference to the AccountManagerService instance - * @hide - */ - public static AccountManagerService getSingleton() { - return sThis.get(); - } - - public AccountManagerService(Context context) { - this(context, context.getPackageManager(), new AccountAuthenticatorCache(context)); - } - - public AccountManagerService(Context context, PackageManager packageManager, - IAccountAuthenticatorCache authenticatorCache) { - mContext = context; - mPackageManager = packageManager; - - mMessageThread = new HandlerThread("AccountManagerService"); - mMessageThread.start(); - mMessageHandler = new MessageHandler(mMessageThread.getLooper()); - - mAuthenticatorCache = authenticatorCache; - mAuthenticatorCache.setListener(this, null /* Handler */); - - sThis.set(this); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - intentFilter.addDataScheme("package"); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context1, Intent intent) { - purgeOldGrantsAll(); - } - }, intentFilter); - - IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent); - } - }, userFilter); - } - - public void systemReady() { - } - - private UserManager getUserManager() { - if (mUserManager == null) { - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - } - return mUserManager; - } - - private UserAccounts initUser(int userId) { - synchronized (mUsers) { - UserAccounts accounts = mUsers.get(userId); - if (accounts == null) { - accounts = new UserAccounts(mContext, userId); - mUsers.append(userId, accounts); - purgeOldGrants(accounts); - validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); - } - return accounts; - } - } - - private void purgeOldGrantsAll() { - synchronized (mUsers) { - for (int i = 0; i < mUsers.size(); i++) { - purgeOldGrants(mUsers.valueAt(i)); - } - } - } - - private void purgeOldGrants(UserAccounts accounts) { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - final Cursor cursor = db.query(TABLE_GRANTS, - new String[]{GRANTS_GRANTEE_UID}, - null, null, GRANTS_GRANTEE_UID, null, null); - try { - while (cursor.moveToNext()) { - final int uid = cursor.getInt(0); - final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; - if (packageExists) { - continue; - } - Log.d(TAG, "deleting grants for UID " + uid - + " because its package is no longer installed"); - db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", - new String[]{Integer.toString(uid)}); - } - } finally { - cursor.close(); - } - } - } - - /** - * Validate internal set of accounts against installed authenticators for - * given user. Clears cached authenticators before validating. - */ - public void validateAccounts(int userId) { - final UserAccounts accounts = getUserAccounts(userId); - - // Invalidate user-specific cache to make sure we catch any - // removed authenticators. - validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); - } - - /** - * Validate internal set of accounts against installed authenticators for - * given user. Clear cached authenticators before validating when requested. - */ - private void validateAccountsInternal( - UserAccounts accounts, boolean invalidateAuthenticatorCache) { - if (invalidateAuthenticatorCache) { - mAuthenticatorCache.invalidateCache(accounts.userId); - } - - final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet(); - for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : - mAuthenticatorCache.getAllServices(accounts.userId)) { - knownAuth.add(service.type); - } - - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - boolean accountDeleted = false; - Cursor cursor = db.query(TABLE_ACCOUNTS, - new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, - null, null, null, null, null); - try { - accounts.accountCache.clear(); - final HashMap<String, ArrayList<String>> accountNamesByType = - new LinkedHashMap<String, ArrayList<String>>(); - while (cursor.moveToNext()) { - final long accountId = cursor.getLong(0); - final String accountType = cursor.getString(1); - final String accountName = cursor.getString(2); - - if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) { - Slog.w(TAG, "deleting account " + accountName + " because type " - + accountType + " no longer has a registered authenticator"); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); - accountDeleted = true; - final Account account = new Account(accountName, accountType); - accounts.userDataCache.remove(account); - accounts.authTokenCache.remove(account); - } else { - ArrayList<String> accountNames = accountNamesByType.get(accountType); - if (accountNames == null) { - accountNames = new ArrayList<String>(); - accountNamesByType.put(accountType, accountNames); - } - accountNames.add(accountName); - } - } - for (Map.Entry<String, ArrayList<String>> cur - : accountNamesByType.entrySet()) { - final String accountType = cur.getKey(); - final ArrayList<String> accountNames = cur.getValue(); - final Account[] accountsForType = new Account[accountNames.size()]; - int i = 0; - for (String accountName : accountNames) { - accountsForType[i] = new Account(accountName, accountType); - ++i; - } - accounts.accountCache.put(accountType, accountsForType); - } - } finally { - cursor.close(); - if (accountDeleted) { - sendAccountsChangedBroadcast(accounts.userId); - } - } - } - } - - private UserAccounts getUserAccountsForCaller() { - return getUserAccounts(UserHandle.getCallingUserId()); - } - - protected UserAccounts getUserAccounts(int userId) { - synchronized (mUsers) { - UserAccounts accounts = mUsers.get(userId); - if (accounts == null) { - accounts = initUser(userId); - mUsers.append(userId, accounts); - } - return accounts; - } - } - - private void onUserRemoved(Intent intent) { - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId < 1) return; - - UserAccounts accounts; - synchronized (mUsers) { - accounts = mUsers.get(userId); - mUsers.remove(userId); - } - if (accounts == null) { - File dbFile = new File(getDatabaseName(userId)); - dbFile.delete(); - return; - } - - synchronized (accounts.cacheLock) { - accounts.openHelper.close(); - File dbFile = new File(getDatabaseName(userId)); - dbFile.delete(); - } - } - - @Override - public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { - Slog.d(TAG, "onServiceChanged() for userId " + userId); - validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */); - } - - public String getPassword(Account account) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getPassword: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - return readPasswordInternal(accounts, account); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private String readPasswordInternal(UserAccounts accounts, Account account) { - if (account == null) { - return null; - } - - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, - ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getString(0); - } - return null; - } finally { - cursor.close(); - } - } - } - - public String getUserData(Account account, String key) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getUserData: " + account - + ", key " + key - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - if (key == null) throw new IllegalArgumentException("key is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - return readUserDataInternal(accounts, account, key); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public AuthenticatorDescription[] getAuthenticatorTypes() { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAuthenticatorTypes: " - + "caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - final int userId = UserHandle.getCallingUserId(); - final long identityToken = clearCallingIdentity(); - try { - Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> - authenticatorCollection = mAuthenticatorCache.getAllServices(userId); - AuthenticatorDescription[] types = - new AuthenticatorDescription[authenticatorCollection.size()]; - int i = 0; - for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator - : authenticatorCollection) { - types[i] = authenticator.type; - i++; - } - return types; - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean addAccount(Account account, String password, Bundle extras) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addAccount: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - - UserAccounts accounts = getUserAccountsForCaller(); - // fails if the account already exists - long identityToken = clearCallingIdentity(); - try { - return addAccountInternal(accounts, account, password, extras); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private boolean addAccountInternal(UserAccounts accounts, Account account, String password, - Bundle extras) { - if (account == null) { - return false; - } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long numMatches = DatabaseUtils.longForQuery(db, - "select count(*) from " + TABLE_ACCOUNTS - + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); - if (numMatches > 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping since the account already exists"); - return false; - } - ContentValues values = new ContentValues(); - values.put(ACCOUNTS_NAME, account.name); - values.put(ACCOUNTS_TYPE, account.type); - values.put(ACCOUNTS_PASSWORD, password); - long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); - if (accountId < 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping the DB insert failed"); - return false; - } - if (extras != null) { - for (String key : extras.keySet()) { - final String value = extras.getString(key); - if (insertExtraLocked(db, accountId, key, value) < 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping since insertExtra failed for key " + key); - return false; - } - } - } - db.setTransactionSuccessful(); - insertAccountIntoCacheLocked(accounts, account); - } finally { - db.endTransaction(); - } - sendAccountsChangedBroadcast(accounts.userId); - return true; - } - } - - private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) { - ContentValues values = new ContentValues(); - values.put(EXTRAS_KEY, key); - values.put(EXTRAS_ACCOUNTS_ID, accountId); - values.put(EXTRAS_VALUE, value); - return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values); - } - - public void hasFeatures(IAccountManagerResponse response, - Account account, String[] features) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "hasFeatures: " + account - + ", response " + response - + ", features " + stringArrayToString(features) - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - if (features == null) throw new IllegalArgumentException("features is null"); - checkReadAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - new TestFeaturesSession(accounts, response, account, features).bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private class TestFeaturesSession extends Session { - private final String[] mFeatures; - private final Account mAccount; - - public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, - Account account, String[] features) { - super(accounts, response, account.type, false /* expectActivityLaunch */, - true /* stripAuthTokenFromResult */); - mFeatures = features; - mAccount = account; - } - - public void run() throws RemoteException { - try { - mAuthenticator.hasFeatures(this, mAccount, mFeatures); - } catch (RemoteException e) { - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); - } - } - - public void onResult(Bundle result) { - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - if (result == null) { - response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); - return; - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - final Bundle newResult = new Bundle(); - newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, - result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); - response.onResult(newResult); - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - } - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", hasFeatures" - + ", " + mAccount - + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); - } - } - - public void removeAccount(IAccountManagerResponse response, Account account) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "removeAccount: " + account - + ", response " + response - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - checkManageAccountsPermission(); - UserHandle user = Binder.getCallingUserHandle(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - - cancelNotification(getSigninRequiredNotificationId(accounts, account), user); - synchronized(accounts.credentialsPermissionNotificationIds) { - for (Pair<Pair<Account, String>, Integer> pair: - accounts.credentialsPermissionNotificationIds.keySet()) { - if (account.equals(pair.first.first)) { - int id = accounts.credentialsPermissionNotificationIds.get(pair); - cancelNotification(id, user); - } - } - } - - try { - new RemoveAccountSession(accounts, response, account).bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private class RemoveAccountSession extends Session { - final Account mAccount; - public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, - Account account) { - super(accounts, response, account.type, false /* expectActivityLaunch */, - true /* stripAuthTokenFromResult */); - mAccount = account; - } - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", removeAccount" - + ", account " + mAccount; - } - - public void run() throws RemoteException { - mAuthenticator.getAccountRemovalAllowed(this, mAccount); - } - - public void onResult(Bundle result) { - if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) - && !result.containsKey(AccountManager.KEY_INTENT)) { - final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); - if (removalAllowed) { - removeAccountInternal(mAccounts, mAccount); - } - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - Bundle result2 = new Bundle(); - result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed); - try { - response.onResult(result2); - } catch (RemoteException e) { - // ignore - } - } - } - super.onResult(result); - } - } - - /* For testing */ - protected void removeAccountInternal(Account account) { - removeAccountInternal(getUserAccountsForCaller(), account); - } - - private void removeAccountInternal(UserAccounts accounts, Account account) { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); - removeAccountFromCacheLocked(accounts, account); - sendAccountsChangedBroadcast(accounts.userId); - } - } - - public void invalidateAuthToken(String accountType, String authToken) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "invalidateAuthToken: accountType " + accountType - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - if (authToken == null) throw new IllegalArgumentException("authToken is null"); - checkManageAccountsOrUseCredentialsPermissions(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - invalidateAuthTokenLocked(accounts, db, accountType, authToken); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, - String accountType, String authToken) { - if (authToken == null || accountType == null) { - return; - } - Cursor cursor = db.rawQuery( - "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID - + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME - + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE - + " FROM " + TABLE_ACCOUNTS - + " JOIN " + TABLE_AUTHTOKENS - + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID - + " = " + AUTHTOKENS_ACCOUNTS_ID - + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND " - + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", - new String[]{authToken, accountType}); - try { - while (cursor.moveToNext()) { - long authTokenId = cursor.getLong(0); - String accountName = cursor.getString(1); - String authTokenType = cursor.getString(2); - db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); - writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType), - authTokenType, null); - } - } finally { - cursor.close(); - } - } - - private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, - String authToken) { - if (account == null || type == null) { - return false; - } - cancelNotification(getSigninRequiredNotificationId(accounts, account), - new UserHandle(accounts.userId)); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId < 0) { - return false; - } - db.delete(TABLE_AUTHTOKENS, - AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", - new String[]{type}); - ContentValues values = new ContentValues(); - values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); - values.put(AUTHTOKENS_TYPE, type); - values.put(AUTHTOKENS_AUTHTOKEN, authToken); - if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { - db.setTransactionSuccessful(); - writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); - return true; - } - return false; - } finally { - db.endTransaction(); - } - } - } - - public String peekAuthToken(Account account, String authTokenType) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "peekAuthToken: " + account - + ", authTokenType " + authTokenType - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - return readAuthTokenInternal(accounts, account, authTokenType); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void setAuthToken(Account account, String authTokenType, String authToken) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "setAuthToken: " + account - + ", authTokenType " + authTokenType - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void setPassword(Account account, String password) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "setAuthToken: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - setPasswordInternal(accounts, account, password); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void setPasswordInternal(UserAccounts accounts, Account account, String password) { - if (account == null) { - return; - } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - final ContentValues values = new ContentValues(); - values.put(ACCOUNTS_PASSWORD, password); - final long accountId = getAccountIdLocked(db, account); - if (accountId >= 0) { - final String[] argsAccountId = {String.valueOf(accountId)}; - db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); - db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); - accounts.authTokenCache.remove(account); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - sendAccountsChangedBroadcast(accounts.userId); - } - } - - private void sendAccountsChangedBroadcast(int userId) { - Log.i(TAG, "the accounts changed, sending broadcast of " - + ACCOUNTS_CHANGED_INTENT.getAction()); - mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); - } - - public void clearPassword(Account account) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "clearPassword: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - setPasswordInternal(accounts, account, null); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void setUserData(Account account, String key, String value) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "setUserData: " + account - + ", key " + key - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (key == null) throw new IllegalArgumentException("key is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - setUserdataInternal(accounts, account, key, value); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void setUserdataInternal(UserAccounts accounts, Account account, String key, - String value) { - if (account == null || key == null) { - return; - } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId < 0) { - return; - } - long extrasId = getExtrasIdLocked(db, accountId, key); - if (extrasId < 0 ) { - extrasId = insertExtraLocked(db, accountId, key, value); - if (extrasId < 0) { - return; - } - } else { - ContentValues values = new ContentValues(); - values.put(EXTRAS_VALUE, value); - if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { - return; - } - - } - writeUserDataIntoCacheLocked(accounts, db, account, key, value); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - } - - private void onResult(IAccountManagerResponse response, Bundle result) { - if (result == null) { - Log.e(TAG, "the result is unexpectedly null", new Exception()); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - try { - response.onResult(result); - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote - // exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - - public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, - final String authTokenType) - throws RemoteException { - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - - final int callingUid = getCallingUid(); - clearCallingIdentity(); - if (callingUid != android.os.Process.SYSTEM_UID) { - throw new SecurityException("can only call from system"); - } - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, accountType, false, - false /* stripAuthTokenFromResult */) { - protected String toDebugString(long now) { - return super.toDebugString(now) + ", getAuthTokenLabel" - + ", " + accountType - + ", authTokenType " + authTokenType; - } - - public void run() throws RemoteException { - mAuthenticator.getAuthTokenLabel(this, authTokenType); - } - - public void onResult(Bundle result) { - if (result != null) { - String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); - Bundle bundle = new Bundle(); - bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); - super.onResult(bundle); - return; - } else { - super.onResult(result); - } - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void getAuthToken(IAccountManagerResponse response, final Account account, - final String authTokenType, final boolean notifyOnAuthFailure, - final boolean expectActivityLaunch, Bundle loginOptionsIn) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAuthToken: " + account - + ", response " + response - + ", authTokenType " + authTokenType - + ", notifyOnAuthFailure " + notifyOnAuthFailure - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkBinderPermission(Manifest.permission.USE_CREDENTIALS); - final UserAccounts accounts = getUserAccountsForCaller(); - final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; - authenticatorInfo = mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(account.type), accounts.userId); - final boolean customTokens = - authenticatorInfo != null && authenticatorInfo.type.customTokens; - - // skip the check if customTokens - final int callerUid = Binder.getCallingUid(); - final boolean permissionGranted = customTokens || - permissionIsGranted(account, authTokenType, callerUid); - - final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() : - loginOptionsIn; - // let authenticator know the identity of the caller - loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); - loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); - if (notifyOnAuthFailure) { - loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); - } - - long identityToken = clearCallingIdentity(); - try { - // if the caller has permission, do the peek. otherwise go the more expensive - // route of starting a Session - if (!customTokens && permissionGranted) { - String authToken = readAuthTokenInternal(accounts, account, authTokenType); - if (authToken != null) { - Bundle result = new Bundle(); - result.putString(AccountManager.KEY_AUTHTOKEN, authToken); - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); - onResult(response, result); - return; - } - } - - new Session(accounts, response, account.type, expectActivityLaunch, - false /* stripAuthTokenFromResult */) { - protected String toDebugString(long now) { - if (loginOptions != null) loginOptions.keySet(); - return super.toDebugString(now) + ", getAuthToken" - + ", " + account - + ", authTokenType " + authTokenType - + ", loginOptions " + loginOptions - + ", notifyOnAuthFailure " + notifyOnAuthFailure; - } - - public void run() throws RemoteException { - // If the caller doesn't have permission then create and return the - // "grant permission" intent instead of the "getAuthToken" intent. - if (!permissionGranted) { - mAuthenticator.getAuthTokenLabel(this, authTokenType); - } else { - mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); - } - } - - public void onResult(Bundle result) { - if (result != null) { - if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { - Intent intent = newGrantCredentialsPermissionIntent(account, callerUid, - new AccountAuthenticatorResponse(this), - authTokenType, - result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL)); - Bundle bundle = new Bundle(); - bundle.putParcelable(AccountManager.KEY_INTENT, intent); - onResult(bundle); - return; - } - String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); - if (authToken != null) { - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { - onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, - "the type and name should not be empty"); - return; - } - if (!customTokens) { - saveAuthTokenToDatabase(mAccounts, new Account(name, type), - authTokenType, authToken); - } - } - - Intent intent = result.getParcelable(AccountManager.KEY_INTENT); - if (intent != null && notifyOnAuthFailure && !customTokens) { - doNotification(mAccounts, - account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), - intent, accounts.userId); - } - } - super.onResult(result); - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void createNoCredentialsPermissionNotification(Account account, Intent intent, - int userId) { - int uid = intent.getIntExtra( - GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); - String authTokenType = intent.getStringExtra( - GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); - String authTokenLabel = intent.getStringExtra( - GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL); - - Notification n = new Notification(android.R.drawable.stat_sys_warning, null, - 0 /* when */); - final String titleAndSubtitle = - mContext.getString(R.string.permission_request_notification_with_subtitle, - account.name); - final int index = titleAndSubtitle.indexOf('\n'); - String title = titleAndSubtitle; - String subtitle = ""; - if (index > 0) { - title = titleAndSubtitle.substring(0, index); - subtitle = titleAndSubtitle.substring(index + 1); - } - UserHandle user = new UserHandle(userId); - n.setLatestEventInfo(mContext, title, subtitle, - PendingIntent.getActivityAsUser(mContext, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT, null, user)); - installNotification(getCredentialPermissionNotificationId( - account, authTokenType, uid), n, user); - } - - private Intent newGrantCredentialsPermissionIntent(Account account, int uid, - AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { - - Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); - // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. - // Since it was set in Eclair+ we can't change it without breaking apps using - // the intent from a non-Activity context. - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory( - String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid))); - - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); - - return intent; - } - - private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, - int uid) { - Integer id; - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - synchronized (accounts.credentialsPermissionNotificationIds) { - final Pair<Pair<Account, String>, Integer> key = - new Pair<Pair<Account, String>, Integer>( - new Pair<Account, String>(account, authTokenType), uid); - id = accounts.credentialsPermissionNotificationIds.get(key); - if (id == null) { - id = mNotificationIds.incrementAndGet(); - accounts.credentialsPermissionNotificationIds.put(key, id); - } - } - return id; - } - - private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { - Integer id; - synchronized (accounts.signinRequiredNotificationIds) { - id = accounts.signinRequiredNotificationIds.get(account); - if (id == null) { - id = mNotificationIds.incrementAndGet(); - accounts.signinRequiredNotificationIds.put(account, id); - } - } - return id; - } - - public void addAcount(final IAccountManagerResponse response, final String accountType, - final String authTokenType, final String[] requiredFeatures, - final boolean expectActivityLaunch, final Bundle optionsIn) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addAccount: accountType " + accountType - + ", response " + response - + ", authTokenType " + authTokenType - + ", requiredFeatures " + stringArrayToString(requiredFeatures) - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - checkManageAccountsPermission(); - - UserAccounts accounts = getUserAccountsForCaller(); - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; - options.putInt(AccountManager.KEY_CALLER_UID, uid); - options.putInt(AccountManager.KEY_CALLER_PID, pid); - - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, accountType, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, - options); - } - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", addAccount" - + ", accountType " + accountType - + ", requiredFeatures " - + (requiredFeatures != null - ? TextUtils.join(",", requiredFeatures) - : null); - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - @Override - public void confirmCredentialsAsUser(IAccountManagerResponse response, - final Account account, final Bundle options, final boolean expectActivityLaunch, - int userId) { - // Only allow the system process to read accounts of other users - if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { - throw new SecurityException("User " + UserHandle.getCallingUserId() - + " trying to confirm account credentials for " + userId); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "confirmCredentials: " + account - + ", response " + response - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccounts(userId); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, account.type, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.confirmCredentials(this, account, options); - } - protected String toDebugString(long now) { - return super.toDebugString(now) + ", confirmCredentials" - + ", " + account; - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void updateCredentials(IAccountManagerResponse response, final Account account, - final String authTokenType, final boolean expectActivityLaunch, - final Bundle loginOptions) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "updateCredentials: " + account - + ", response " + response - + ", authTokenType " + authTokenType - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, account.type, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); - } - protected String toDebugString(long now) { - if (loginOptions != null) loginOptions.keySet(); - return super.toDebugString(now) + ", updateCredentials" - + ", " + account - + ", authTokenType " + authTokenType - + ", loginOptions " + loginOptions; - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void editProperties(IAccountManagerResponse response, final String accountType, - final boolean expectActivityLaunch) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "editProperties: accountType " + accountType - + ", response " + response - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, accountType, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.editProperties(this, mAccountType); - } - protected String toDebugString(long now) { - return super.toDebugString(now) + ", editProperties" - + ", accountType " + accountType; - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private class GetAccountsByTypeAndFeatureSession extends Session { - private final String[] mFeatures; - private volatile Account[] mAccountsOfType = null; - private volatile ArrayList<Account> mAccountsWithFeatures = null; - private volatile int mCurrentAccount = 0; - - public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, - IAccountManagerResponse response, String type, String[] features) { - super(accounts, response, type, false /* expectActivityLaunch */, - true /* stripAuthTokenFromResult */); - mFeatures = features; - } - - public void run() throws RemoteException { - synchronized (mAccounts.cacheLock) { - mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType); - } - // check whether each account matches the requested features - mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); - mCurrentAccount = 0; - - checkAccount(); - } - - public void checkAccount() { - if (mCurrentAccount >= mAccountsOfType.length) { - sendResult(); - return; - } - - final IAccountAuthenticator accountAuthenticator = mAuthenticator; - if (accountAuthenticator == null) { - // It is possible that the authenticator has died, which is indicated by - // mAuthenticator being set to null. If this happens then just abort. - // There is no need to send back a result or error in this case since - // that already happened when mAuthenticator was cleared. - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "checkAccount: aborting session since we are no longer" - + " connected to the authenticator, " + toDebugString()); - } - return; - } - try { - accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); - } catch (RemoteException e) { - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); - } - } - - public void onResult(Bundle result) { - mNumResults++; - if (result == null) { - onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); - return; - } - if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { - mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); - } - mCurrentAccount++; - checkAccount(); - } - - public void sendResult() { - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - Account[] accounts = new Account[mAccountsWithFeatures.size()]; - for (int i = 0; i < accounts.length; i++) { - accounts[i] = mAccountsWithFeatures.get(i); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - Bundle result = new Bundle(); - result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); - response.onResult(result); - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - } - - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" - + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); - } - } - - /** - * Returns the accounts for a specific user - * @hide - */ - public Account[] getAccounts(int userId) { - checkReadAccountsPermission(); - UserAccounts accounts = getUserAccounts(userId); - long identityToken = clearCallingIdentity(); - try { - synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, null); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** - * Returns accounts for all running users. - * - * @hide - */ - public AccountAndUser[] getRunningAccounts() { - final int[] runningUserIds; - try { - runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); - } catch (RemoteException e) { - // Running in system_server; should never happen - throw new RuntimeException(e); - } - return getAccounts(runningUserIds); - } - - /** {@hide} */ - public AccountAndUser[] getAllAccounts() { - final List<UserInfo> users = getUserManager().getUsers(); - final int[] userIds = new int[users.size()]; - for (int i = 0; i < userIds.length; i++) { - userIds[i] = users.get(i).id; - } - return getAccounts(userIds); - } - - private AccountAndUser[] getAccounts(int[] userIds) { - final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); - synchronized (mUsers) { - for (int userId : userIds) { - UserAccounts userAccounts = getUserAccounts(userId); - if (userAccounts == null) continue; - synchronized (userAccounts.cacheLock) { - Account[] accounts = getAccountsFromCacheLocked(userAccounts, null); - for (int a = 0; a < accounts.length; a++) { - runningAccounts.add(new AccountAndUser(accounts[a], userId)); - } - } - } - } - - AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; - return runningAccounts.toArray(accountsArray); - } - - @Override - public Account[] getAccountsAsUser(String type, int userId) { - // Only allow the system process to read accounts of other users - if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { - throw new SecurityException("User " + UserHandle.getCallingUserId() - + " trying to get account for " + userId); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAccounts: accountType " + type - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - checkReadAccountsPermission(); - UserAccounts accounts = getUserAccounts(userId); - long identityToken = clearCallingIdentity(); - try { - synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, type); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - @Override - public Account[] getAccounts(String type) { - return getAccountsAsUser(type, UserHandle.getCallingUserId()); - } - - public void getAccountsByFeatures(IAccountManagerResponse response, - String type, String[] features) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAccounts: accountType " + type - + ", response " + response - + ", features " + stringArrayToString(features) - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (type == null) throw new IllegalArgumentException("accountType is null"); - checkReadAccountsPermission(); - UserAccounts userAccounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - if (features == null || features.length == 0) { - Account[] accounts; - synchronized (userAccounts.cacheLock) { - accounts = getAccountsFromCacheLocked(userAccounts, type); - } - Bundle result = new Bundle(); - result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); - onResult(response, result); - return; - } - new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private long getAccountIdLocked(SQLiteDatabase db, Account account) { - Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, - "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getLong(0); - } - return -1; - } finally { - cursor.close(); - } - } - - private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { - Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, - EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", - new String[]{key}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getLong(0); - } - return -1; - } finally { - cursor.close(); - } - } - - private abstract class Session extends IAccountAuthenticatorResponse.Stub - implements IBinder.DeathRecipient, ServiceConnection { - IAccountManagerResponse mResponse; - final String mAccountType; - final boolean mExpectActivityLaunch; - final long mCreationTime; - - public int mNumResults = 0; - private int mNumRequestContinued = 0; - private int mNumErrors = 0; - - - IAccountAuthenticator mAuthenticator = null; - - private final boolean mStripAuthTokenFromResult; - protected final UserAccounts mAccounts; - - public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, - boolean expectActivityLaunch, boolean stripAuthTokenFromResult) { - super(); - if (response == null) throw new IllegalArgumentException("response is null"); - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - mAccounts = accounts; - mStripAuthTokenFromResult = stripAuthTokenFromResult; - mResponse = response; - mAccountType = accountType; - mExpectActivityLaunch = expectActivityLaunch; - mCreationTime = SystemClock.elapsedRealtime(); - synchronized (mSessions) { - mSessions.put(toString(), this); - } - try { - response.asBinder().linkToDeath(this, 0 /* flags */); - } catch (RemoteException e) { - mResponse = null; - binderDied(); - } - } - - IAccountManagerResponse getResponseAndClose() { - if (mResponse == null) { - // this session has already been closed - return null; - } - IAccountManagerResponse response = mResponse; - close(); // this clears mResponse so we need to save the response before this call - return response; - } - - private void close() { - synchronized (mSessions) { - if (mSessions.remove(toString()) == null) { - // the session was already closed, so bail out now - return; - } - } - if (mResponse != null) { - // stop listening for response deaths - mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); - - // clear this so that we don't accidentally send any further results - mResponse = null; - } - cancelTimeout(); - unbind(); - } - - public void binderDied() { - mResponse = null; - close(); - } - - protected String toDebugString() { - return toDebugString(SystemClock.elapsedRealtime()); - } - - protected String toDebugString(long now) { - return "Session: expectLaunch " + mExpectActivityLaunch - + ", connected " + (mAuthenticator != null) - + ", stats (" + mNumResults + "/" + mNumRequestContinued - + "/" + mNumErrors + ")" - + ", lifetime " + ((now - mCreationTime) / 1000.0); - } - - void bind() { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "initiating bind to authenticator type " + mAccountType); - } - if (!bindToAuthenticator(mAccountType)) { - Log.d(TAG, "bind attempt failed for " + toDebugString()); - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); - } - } - - private void unbind() { - if (mAuthenticator != null) { - mAuthenticator = null; - mContext.unbindService(this); - } - } - - public void scheduleTimeout() { - mMessageHandler.sendMessageDelayed( - mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS); - } - - public void cancelTimeout() { - mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); - } - - public void onServiceConnected(ComponentName name, IBinder service) { - mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); - try { - run(); - } catch (RemoteException e) { - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, - "remote exception"); - } - } - - public void onServiceDisconnected(ComponentName name) { - mAuthenticator = null; - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, - "disconnected"); - } catch (RemoteException e) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onServiceDisconnected: " - + "caught RemoteException while responding", e); - } - } - } - } - - public abstract void run() throws RemoteException; - - public void onTimedOut() { - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, - "timeout"); - } catch (RemoteException e) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", - e); - } - } - } - } - - public void onResult(Bundle result) { - mNumResults++; - if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { - String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { - Account account = new Account(accountName, accountType); - cancelNotification(getSigninRequiredNotificationId(mAccounts, account), - new UserHandle(mAccounts.userId)); - } - } - IAccountManagerResponse response; - if (mExpectActivityLaunch && result != null - && result.containsKey(AccountManager.KEY_INTENT)) { - response = mResponse; - } else { - response = getResponseAndClose(); - } - if (response != null) { - try { - if (result == null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() - + " calling onError() on response " + response); - } - response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, - "null bundle returned"); - } else { - if (mStripAuthTokenFromResult) { - result.remove(AccountManager.KEY_AUTHTOKEN); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() - + " calling onResult() on response " + response); - } - response.onResult(result); - } - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - } - - public void onRequestContinued() { - mNumRequestContinued++; - } - - public void onError(int errorCode, String errorMessage) { - mNumErrors++; - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() - + " calling onError() on response " + response); - } - try { - response.onError(errorCode, errorMessage); - } catch (RemoteException e) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onError: caught RemoteException while responding", e); - } - } - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onError: already closed"); - } - } - } - - /** - * find the component name for the authenticator and initiate a bind - * if no authenticator or the bind fails then return false, otherwise return true - */ - private boolean bindToAuthenticator(String authenticatorType) { - final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; - authenticatorInfo = mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); - if (authenticatorInfo == null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "there is no authenticator for " + authenticatorType - + ", bailing out"); - } - return false; - } - - Intent intent = new Intent(); - intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); - intent.setComponent(authenticatorInfo.componentName); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); - } - if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE, mAccounts.userId)) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); - } - return false; - } - - - return true; - } - } - - private class MessageHandler extends Handler { - MessageHandler(Looper looper) { - super(looper); - } - - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_TIMED_OUT: - Session session = (Session)msg.obj; - session.onTimedOut(); - break; - - default: - throw new IllegalStateException("unhandled message: " + msg.what); - } - } - } - - private static String getDatabaseName(int userId) { - File systemDir = Environment.getSystemSecureDirectory(); - File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME); - if (userId == 0) { - // Migrate old file, if it exists, to the new location. - // Make sure the new file doesn't already exist. A dummy file could have been - // accidentally created in the old location, causing the new one to become corrupted - // as well. - File oldFile = new File(systemDir, DATABASE_NAME); - if (oldFile.exists() && !databaseFile.exists()) { - // Check for use directory; create if it doesn't exist, else renameTo will fail - File userDir = Environment.getUserSystemDirectory(userId); - if (!userDir.exists()) { - if (!userDir.mkdirs()) { - throw new IllegalStateException("User dir cannot be created: " + userDir); - } - } - if (!oldFile.renameTo(databaseFile)) { - throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); - } - } - } - return databaseFile.getPath(); - } - - static class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(Context context, int userId) { - super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); - } - - /** - * This call needs to be made while the mCacheLock is held. The way to - * ensure this is to get the lock any time a method is called ont the DatabaseHelper - * @param db The database. - */ - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " - + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + ACCOUNTS_NAME + " TEXT NOT NULL, " - + ACCOUNTS_TYPE + " TEXT NOT NULL, " - + ACCOUNTS_PASSWORD + " TEXT, " - + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); - - db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " - + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " - + AUTHTOKENS_TYPE + " TEXT NOT NULL, " - + AUTHTOKENS_AUTHTOKEN + " TEXT, " - + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); - - createGrantsTable(db); - - db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " - + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + EXTRAS_ACCOUNTS_ID + " INTEGER, " - + EXTRAS_KEY + " TEXT NOT NULL, " - + EXTRAS_VALUE + " TEXT, " - + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); - - db.execSQL("CREATE TABLE " + TABLE_META + " ( " - + META_KEY + " TEXT PRIMARY KEY NOT NULL, " - + META_VALUE + " TEXT)"); - - createAccountsDeletionTrigger(db); - } - - private void createAccountsDeletionTrigger(SQLiteDatabase db) { - db.execSQL("" - + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS - + " BEGIN" - + " DELETE FROM " + TABLE_AUTHTOKENS - + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" - + " DELETE FROM " + TABLE_EXTRAS - + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" - + " DELETE FROM " + TABLE_GRANTS - + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" - + " END"); - } - - private void createGrantsTable(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " - + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " - + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " - + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " - + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE - + "," + GRANTS_GRANTEE_UID + "))"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); - - if (oldVersion == 1) { - // no longer need to do anything since the work is done - // when upgrading from version 2 - oldVersion++; - } - - if (oldVersion == 2) { - createGrantsTable(db); - db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); - createAccountsDeletionTrigger(db); - oldVersion++; - } - - if (oldVersion == 3) { - db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + - " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); - oldVersion++; - } - } - - @Override - public void onOpen(SQLiteDatabase db) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); - } - } - - public IBinder onBind(Intent intent) { - return asBinder(); - } - - /** - * Searches array of arguments for the specified string - * @param args array of argument strings - * @param value value to search for - * @return true if the value is contained in the array - */ - private static boolean scanArgs(String[] args, String value) { - if (args != null) { - for (String arg : args) { - if (value.equals(arg)) { - return true; - } - } - } - return false; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - fout.println("Permission Denial: can't dump AccountsManager from from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " without permission " + android.Manifest.permission.DUMP); - return; - } - final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); - final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); - - final List<UserInfo> users = getUserManager().getUsers(); - for (UserInfo user : users) { - ipw.println("User " + user + ":"); - ipw.increaseIndent(); - dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); - ipw.println(); - ipw.decreaseIndent(); - } - } - - private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, - String[] args, boolean isCheckinRequest) { - synchronized (userAccounts.cacheLock) { - final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); - - if (isCheckinRequest) { - // This is a checkin request. *Only* upload the account types and the count of each. - Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, - null, null, ACCOUNTS_TYPE, null, null); - try { - while (cursor.moveToNext()) { - // print type,count - fout.println(cursor.getString(0) + "," + cursor.getString(1)); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } else { - Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */); - fout.println("Accounts: " + accounts.length); - for (Account account : accounts) { - fout.println(" " + account); - } - - fout.println(); - synchronized (mSessions) { - final long now = SystemClock.elapsedRealtime(); - fout.println("Active Sessions: " + mSessions.size()); - for (Session session : mSessions.values()) { - fout.println(" " + session.toDebugString(now)); - } - } - - fout.println(); - mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); - } - } - } - - private void doNotification(UserAccounts accounts, Account account, CharSequence message, - Intent intent, int userId) { - long identityToken = clearCallingIdentity(); - try { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "doNotification: " + message + " intent:" + intent); - } - - if (intent.getComponent() != null && - GrantCredentialsPermissionActivity.class.getName().equals( - intent.getComponent().getClassName())) { - createNoCredentialsPermissionNotification(account, intent, userId); - } else { - final Integer notificationId = getSigninRequiredNotificationId(accounts, account); - intent.addCategory(String.valueOf(notificationId)); - Notification n = new Notification(android.R.drawable.stat_sys_warning, null, - 0 /* when */); - UserHandle user = new UserHandle(userId); - final String notificationTitleFormat = - mContext.getText(R.string.notification_title).toString(); - n.setLatestEventInfo(mContext, - String.format(notificationTitleFormat, account.name), - message, PendingIntent.getActivityAsUser( - mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, - null, user)); - installNotification(notificationId, n, user); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - protected void installNotification(final int notificationId, final Notification n, - UserHandle user) { - ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .notifyAsUser(null, notificationId, n, user); - } - - protected void cancelNotification(int id, UserHandle user) { - long identityToken = clearCallingIdentity(); - try { - ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .cancelAsUser(null, id, user); - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** Succeeds if any of the specified permissions are granted. */ - private void checkBinderPermission(String... permissions) { - final int uid = Binder.getCallingUid(); - - for (String perm : permissions) { - if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, " caller uid " + uid + " has " + perm); - } - return; - } - } - - String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions); - Log.w(TAG, " " + msg); - throw new SecurityException(msg); - } - - private boolean inSystemImage(int callingUid) { - final int callingUserId = UserHandle.getUserId(callingUid); - - final PackageManager userPackageManager; - try { - userPackageManager = mContext.createPackageContextAsUser( - "android", 0, new UserHandle(callingUserId)).getPackageManager(); - } catch (NameNotFoundException e) { - return false; - } - - String[] packages = userPackageManager.getPackagesForUid(callingUid); - for (String name : packages) { - try { - PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); - if (packageInfo != null - && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return true; - } - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - return false; - } - - private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { - final boolean inSystemImage = inSystemImage(callerUid); - final boolean fromAuthenticator = account != null - && hasAuthenticatorUid(account.type, callerUid); - final boolean hasExplicitGrants = account != null - && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " - + callerUid + ", " + account - + ": is authenticator? " + fromAuthenticator - + ", has explicit permission? " + hasExplicitGrants); - } - return fromAuthenticator || hasExplicitGrants || inSystemImage; - } - - private boolean hasAuthenticatorUid(String accountType, int callingUid) { - final int callingUserId = UserHandle.getUserId(callingUid); - for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : - mAuthenticatorCache.getAllServices(callingUserId)) { - if (serviceInfo.type.type.equals(accountType)) { - return (serviceInfo.uid == callingUid) || - (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) - == PackageManager.SIGNATURE_MATCH); - } - } - return false; - } - - private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, - int callerUid) { - if (callerUid == android.os.Process.SYSTEM_UID) { - return true; - } - UserAccounts accounts = getUserAccountsForCaller(); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - String[] args = { String.valueOf(callerUid), authTokenType, - account.name, account.type}; - final boolean permissionGranted = - DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; - if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { - // TODO: Skip this check when running automated tests. Replace this - // with a more general solution. - Log.d(TAG, "no credentials permission for usage of " + account + ", " - + authTokenType + " by uid " + callerUid - + " but ignoring since device is in test harness."); - return true; - } - return permissionGranted; - } - } - - private void checkCallingUidAgainstAuthenticator(Account account) { - final int uid = Binder.getCallingUid(); - if (account == null || !hasAuthenticatorUid(account.type, uid)) { - String msg = "caller uid " + uid + " is different than the authenticator's uid"; - Log.w(TAG, msg); - throw new SecurityException(msg); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid"); - } - } - - private void checkAuthenticateAccountsPermission(Account account) { - checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS); - checkCallingUidAgainstAuthenticator(account); - } - - private void checkReadAccountsPermission() { - checkBinderPermission(Manifest.permission.GET_ACCOUNTS); - } - - private void checkManageAccountsPermission() { - checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS); - } - - private void checkManageAccountsOrUseCredentialsPermissions() { - checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS, - Manifest.permission.USE_CREDENTIALS); - } - - public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) - throws RemoteException { - final int callingUid = getCallingUid(); - - if (callingUid != android.os.Process.SYSTEM_UID) { - throw new SecurityException(); - } - - if (value) { - grantAppPermission(account, authTokenType, uid); - } else { - revokeAppPermission(account, authTokenType, uid); - } - } - - /** - * Allow callers with the given uid permission to get credentials for account/authTokenType. - * <p> - * Although this is public it can only be accessed via the AccountManagerService object - * which is in the system. This means we don't need to protect it with permissions. - * @hide - */ - private void grantAppPermission(Account account, String authTokenType, int uid) { - if (account == null || authTokenType == null) { - Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); - return; - } - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId >= 0) { - ContentValues values = new ContentValues(); - values.put(GRANTS_ACCOUNTS_ID, accountId); - values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); - values.put(GRANTS_GRANTEE_UID, uid); - db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), - new UserHandle(accounts.userId)); - } - } - - /** - * Don't allow callers with the given uid permission to get credentials for - * account/authTokenType. - * <p> - * Although this is public it can only be accessed via the AccountManagerService object - * which is in the system. This means we don't need to protect it with permissions. - * @hide - */ - private void revokeAppPermission(Account account, String authTokenType, int uid) { - if (account == null || authTokenType == null) { - Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); - return; - } - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId >= 0) { - db.delete(TABLE_GRANTS, - GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " - + GRANTS_GRANTEE_UID + "=?", - new String[]{String.valueOf(accountId), authTokenType, - String.valueOf(uid)}); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), - new UserHandle(accounts.userId)); - } - } - - static final private String stringArrayToString(String[] value) { - return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; - } - - private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { - final Account[] oldAccountsForType = accounts.accountCache.get(account.type); - if (oldAccountsForType != null) { - ArrayList<Account> newAccountsList = new ArrayList<Account>(); - for (Account curAccount : oldAccountsForType) { - if (!curAccount.equals(account)) { - newAccountsList.add(curAccount); - } - } - if (newAccountsList.isEmpty()) { - accounts.accountCache.remove(account.type); - } else { - Account[] newAccountsForType = new Account[newAccountsList.size()]; - newAccountsForType = newAccountsList.toArray(newAccountsForType); - accounts.accountCache.put(account.type, newAccountsForType); - } - } - accounts.userDataCache.remove(account); - accounts.authTokenCache.remove(account); - } - - /** - * This assumes that the caller has already checked that the account is not already present. - */ - private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { - Account[] accountsForType = accounts.accountCache.get(account.type); - int oldLength = (accountsForType != null) ? accountsForType.length : 0; - Account[] newAccountsForType = new Account[oldLength + 1]; - if (accountsForType != null) { - System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); - } - newAccountsForType[oldLength] = account; - accounts.accountCache.put(account.type, newAccountsForType); - } - - protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) { - if (accountType != null) { - final Account[] accounts = userAccounts.accountCache.get(accountType); - if (accounts == null) { - return EMPTY_ACCOUNT_ARRAY; - } else { - return Arrays.copyOf(accounts, accounts.length); - } - } else { - int totalLength = 0; - for (Account[] accounts : userAccounts.accountCache.values()) { - totalLength += accounts.length; - } - if (totalLength == 0) { - return EMPTY_ACCOUNT_ARRAY; - } - Account[] accounts = new Account[totalLength]; - totalLength = 0; - for (Account[] accountsOfType : userAccounts.accountCache.values()) { - System.arraycopy(accountsOfType, 0, accounts, totalLength, - accountsOfType.length); - totalLength += accountsOfType.length; - } - return accounts; - } - } - - protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, - Account account, String key, String value) { - HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); - if (userDataForAccount == null) { - userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); - accounts.userDataCache.put(account, userDataForAccount); - } - if (value == null) { - userDataForAccount.remove(key); - } else { - userDataForAccount.put(key, value); - } - } - - protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, - Account account, String key, String value) { - HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); - if (authTokensForAccount == null) { - authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); - accounts.authTokenCache.put(account, authTokensForAccount); - } - if (value == null) { - authTokensForAccount.remove(key); - } else { - authTokensForAccount.put(key, value); - } - } - - protected String readAuthTokenInternal(UserAccounts accounts, Account account, - String authTokenType) { - synchronized (accounts.cacheLock) { - HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); - if (authTokensForAccount == null) { - // need to populate the cache for this account - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); - accounts.authTokenCache.put(account, authTokensForAccount); - } - return authTokensForAccount.get(authTokenType); - } - } - - protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { - synchronized (accounts.cacheLock) { - HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); - if (userDataForAccount == null) { - // need to populate the cache for this account - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); - accounts.userDataCache.put(account, userDataForAccount); - } - return userDataForAccount.get(key); - } - } - - protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( - final SQLiteDatabase db, Account account) { - HashMap<String, String> userDataForAccount = new HashMap<String, String>(); - Cursor cursor = db.query(TABLE_EXTRAS, - COLUMNS_EXTRAS_KEY_AND_VALUE, - SELECTION_USERDATA_BY_ACCOUNT, - new String[]{account.name, account.type}, - null, null, null); - try { - while (cursor.moveToNext()) { - final String tmpkey = cursor.getString(0); - final String value = cursor.getString(1); - userDataForAccount.put(tmpkey, value); - } - } finally { - cursor.close(); - } - return userDataForAccount; - } - - protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( - final SQLiteDatabase db, Account account) { - HashMap<String, String> authTokensForAccount = new HashMap<String, String>(); - Cursor cursor = db.query(TABLE_AUTHTOKENS, - COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, - SELECTION_AUTHTOKENS_BY_ACCOUNT, - new String[]{account.name, account.type}, - null, null, null); - try { - while (cursor.moveToNext()) { - final String type = cursor.getString(0); - final String authToken = cursor.getString(1); - authTokensForAccount.put(type, authToken); - } - } finally { - cursor.close(); - } - return authTokensForAccount; - } -} diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java deleted file mode 100644 index 06c2106..0000000 --- a/core/java/android/accounts/IAccountAuthenticatorCache.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCacheListener; -import android.os.Handler; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.Collection; - -/** - * An interface to the Authenticator specialization of RegisteredServicesCache. The use of - * this interface by the AccountManagerService makes it easier to unit test it. - * @hide - */ -public interface IAccountAuthenticatorCache { - /** - * Accessor for the {@link android.content.pm.RegisteredServicesCache.ServiceInfo} that - * matched the specified {@link android.accounts.AuthenticatorDescription} or null - * if none match. - * @param type the authenticator type to return - * @return the {@link android.content.pm.RegisteredServicesCache.ServiceInfo} that - * matches the account type or null if none is present - */ - RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> getServiceInfo( - AuthenticatorDescription type, int userId); - - /** - * @return A copy of a Collection of all the current Authenticators. - */ - Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices( - int userId); - - /** - * Dumps the state of the cache. See - * {@link android.os.Binder#dump(java.io.FileDescriptor, java.io.PrintWriter, String[])} - */ - void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId); - - /** - * Sets a listener that will be notified whenever the authenticator set changes - * @param listener the listener to notify, or null - * @param handler the {@link Handler} on which the notification will be posted. If null - * the notification will be posted on the main thread. - */ - void setListener(RegisteredServicesCacheListener<AuthenticatorDescription> listener, - Handler handler); - - void invalidateCache(int userId); -} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d6ddeb6..87c2d8c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1346,6 +1346,20 @@ public class Activity extends ContextThemeWrapper } /** + * This is called when the user is requesting an assist, to build a full + * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current + * application. You can override this method to place into the bundle anything + * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part + * of the assist Intent. The default implementation does nothing. + * + * <p>This function will be called after any global assist callbacks that had + * been registered with {@link Application#registerOnProvideAssistData + * Application.registerOnProvideAssistData}. + */ + public void onProvideAssistData(Bundle data) { + } + + /** * Called when you are no longer visible to the user. You will next * receive either {@link #onRestart}, {@link #onDestroy}, or nothing, * depending on later user activity. @@ -3726,7 +3740,7 @@ public class Activity extends ContextThemeWrapper try { intent.setAllowFds(false); result = ActivityManagerNative.getDefault() - .startActivity(mMainThread.getApplicationThread(), + .startActivity(mMainThread.getApplicationThread(), getBasePackageName(), intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID, requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, null, diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 594be68..944a533 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1915,7 +1915,30 @@ public class ActivityManager { return PackageManager.PERMISSION_DENIED; } - /** @hide */ + /** + * @hide + * Helper for dealing with incoming user arguments to system service calls. + * Takes care of checking permissions and converting USER_CURRENT to the + * actual current user. + * + * @param callingPid The pid of the incoming call, as per Binder.getCallingPid(). + * @param callingUid The uid of the incoming call, as per Binder.getCallingUid(). + * @param userId The user id argument supplied by the caller -- this is the user + * they want to run as. + * @param allowAll If true, we will allow USER_ALL. This means you must be prepared + * to get a USER_ALL returned and deal with it correctly. If false, + * an exception will be thrown if USER_ALL is supplied. + * @param requireFull If true, the caller must hold + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} to be able to run as a + * different user than their current process; otherwise they must hold + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS}. + * @param name Optional textual name of the incoming call; only for generating error messages. + * @param callerPackage Optional package name of caller; only for error messages. + * + * @return Returns the user ID that the call should run as. Will always be a concrete + * user number, unless <var>allowAll</var> is true in which case it could also be + * USER_ALL. + */ public static int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, String name, String callerPackage) { if (UserHandle.getUserId(callingUid) == userId) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index fe7338b..aca4f9c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -39,7 +39,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; -import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Singleton; @@ -93,7 +92,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM try { getDefault().broadcastIntent( null, intent, null, null, Activity.RESULT_OK, null, null, - null /*permission*/, false, true, userId); + null /*permission*/, AppOpsManager.OP_NONE, false, true, userId); } catch (RemoteException ex) { } } @@ -117,6 +116,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); + String callingPackage = data.readString(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); @@ -128,7 +128,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM ? data.readFileDescriptor() : null; Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; - int result = startActivity(app, intent, resolvedType, + int result = startActivity(app, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, options); reply.writeNoException(); @@ -141,6 +141,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); + String callingPackage = data.readString(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); @@ -153,7 +154,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int userId = data.readInt(); - int result = startActivityAsUser(app, intent, resolvedType, + int result = startActivityAsUser(app, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, options, userId); reply.writeNoException(); @@ -166,6 +167,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); + String callingPackage = data.readString(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); @@ -178,7 +180,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int userId = data.readInt(); - WaitResult result = startActivityAndWait(app, intent, resolvedType, + WaitResult result = startActivityAndWait(app, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, options, userId); reply.writeNoException(); @@ -191,6 +193,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); + String callingPackage = data.readString(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); @@ -201,7 +204,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int userId = data.readInt(); - int result = startActivityWithConfig(app, intent, resolvedType, + int result = startActivityWithConfig(app, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, config, options, userId); reply.writeNoException(); reply.writeInt(result); @@ -341,11 +344,12 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM String resultData = data.readString(); Bundle resultExtras = data.readBundle(); String perm = data.readString(); + int appOp = data.readInt(); boolean serialized = data.readInt() != 0; boolean sticky = data.readInt() != 0; int userId = data.readInt(); int res = broadcastIntent(app, intent, resolvedType, resultTo, - resultCode, resultData, resultExtras, perm, + resultCode, resultData, resultExtras, perm, appOp, serialized, sticky, userId); reply.writeNoException(); reply.writeInt(res); @@ -836,8 +840,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM Bundle arguments = data.readBundle(); IBinder b = data.readStrongBinder(); IInstrumentationWatcher w = IInstrumentationWatcher.Stub.asInterface(b); + b = data.readStrongBinder(); + IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b); int userId = data.readInt(); - boolean res = startInstrumentation(className, profileFile, fl, arguments, w, userId); + boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -1526,13 +1532,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); + String callingPackage = data.readString(); Intent[] intents = data.createTypedArray(Intent.CREATOR); String[] resolvedTypes = data.createStringArray(); IBinder resultTo = data.readStrongBinder(); Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int userId = data.readInt(); - int result = startActivities(app, intents, resolvedTypes, resultTo, + int result = startActivities(app, callingPackage, intents, resolvedTypes, resultTo, options, userId); reply.writeNoException(); reply.writeInt(result); @@ -1784,6 +1791,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_LAUNCHED_FROM_PACKAGE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + String res = getLaunchedFromPackage(token); + reply.writeNoException(); + reply.writeString(res); + return true; + } + case REGISTER_USER_SWITCH_OBSERVER_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IUserSwitchObserver observer = IUserSwitchObserver.Stub.asInterface( @@ -1819,6 +1835,24 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_TOP_ACTIVITY_EXTRAS_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int requestType = data.readInt(); + Bundle res = getTopActivityExtras(requestType); + reply.writeNoException(); + reply.writeBundle(res); + return true; + } + + case REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + Bundle extras = data.readBundle(); + reportTopActivityExtras(token, extras); + reply.writeNoException(); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -1855,7 +1889,7 @@ class ActivityManagerProxy implements IActivityManager return mRemote; } - public int startActivity(IApplicationThread caller, Intent intent, + public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) throws RemoteException { @@ -1863,6 +1897,7 @@ class ActivityManagerProxy implements IActivityManager Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(callingPackage); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo); @@ -1890,7 +1925,7 @@ class ActivityManagerProxy implements IActivityManager return result; } - public int startActivityAsUser(IApplicationThread caller, Intent intent, + public int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException { @@ -1898,6 +1933,7 @@ class ActivityManagerProxy implements IActivityManager Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(callingPackage); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo); @@ -1925,14 +1961,15 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); return result; } - public WaitResult startActivityAndWait(IApplicationThread caller, Intent intent, - String resolvedType, IBinder resultTo, String resultWho, + public WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, + Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(callingPackage); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo); @@ -1960,14 +1997,15 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); return result; } - public int startActivityWithConfig(IApplicationThread caller, Intent intent, - String resolvedType, IBinder resultTo, String resultWho, + public int startActivityWithConfig(IApplicationThread caller, String callingPackage, + Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Configuration config, Bundle options, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(callingPackage); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo); @@ -2138,7 +2176,7 @@ class ActivityManagerProxy implements IActivityManager public int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, - String requiredPermission, boolean serialized, + String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) throws RemoteException { Parcel data = Parcel.obtain(); @@ -2152,6 +2190,7 @@ class ActivityManagerProxy implements IActivityManager data.writeString(resultData); data.writeBundle(map); data.writeString(requiredPermission); + data.writeInt(appOp); data.writeInt(serialized ? 1 : 0); data.writeInt(sticky ? 1 : 0); data.writeInt(userId); @@ -2857,8 +2896,8 @@ class ActivityManagerProxy implements IActivityManager } public boolean startInstrumentation(ComponentName className, String profileFile, - int flags, Bundle arguments, IInstrumentationWatcher watcher, int userId) - throws RemoteException { + int flags, Bundle arguments, IInstrumentationWatcher watcher, + IUiAutomationConnection connection, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -2867,6 +2906,7 @@ class ActivityManagerProxy implements IActivityManager data.writeInt(flags); data.writeBundle(arguments); data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); + data.writeStrongBinder(connection != null ? connection.asBinder() : null); data.writeInt(userId); mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0); reply.readException(); @@ -3752,13 +3792,14 @@ class ActivityManagerProxy implements IActivityManager return res; } - public int startActivities(IApplicationThread caller, + public int startActivities(IApplicationThread caller, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(callingPackage); data.writeTypedArray(intents, 0); data.writeStringArray(resolvedTypes); data.writeStrongBinder(resultTo); @@ -4104,6 +4145,19 @@ class ActivityManagerProxy implements IActivityManager return result; } + public String getLaunchedFromPackage(IBinder activityToken) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(activityToken); + mRemote.transact(GET_LAUNCHED_FROM_PACKAGE_TRANSACTION, data, reply, 0); + reply.readException(); + String result = reply.readString(); + data.recycle(); + reply.recycle(); + return result; + } + public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -4150,5 +4204,30 @@ class ActivityManagerProxy implements IActivityManager return res; } + public Bundle getTopActivityExtras(int requestType) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(requestType); + mRemote.transact(GET_TOP_ACTIVITY_EXTRAS_TRANSACTION, data, reply, 0); + reply.readException(); + Bundle res = reply.readBundle(); + data.recycle(); + reply.recycle(); + return res; + } + + public void reportTopActivityExtras(IBinder token, Bundle extras) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + data.writeBundle(extras); + mRemote.transact(REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d880817..bb73cf4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -43,7 +43,6 @@ import android.database.sqlite.SQLiteDebug; import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.net.IConnectivityManager; import android.net.Proxy; @@ -102,7 +101,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.net.InetAddress; -import java.security.Security; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -187,7 +185,8 @@ public final class ActivityThread { = new ArrayList<Application>(); // set of instantiated backup agents, keyed by package name final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>(); - static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal<ActivityThread>(); + /** Reference to singleton {@link ActivityThread} */ + private static ActivityThread sCurrentActivityThread; Instrumentation mInstrumentation; String mInstrumentationAppDir = null; String mInstrumentationAppLibraryDir = null; @@ -419,6 +418,7 @@ public final class ActivityThread { ComponentName instrumentationName; Bundle instrumentationArgs; IInstrumentationWatcher instrumentationWatcher; + IUiAutomationConnection instrumentationUiAutomationConnection; int debugMode; boolean enableOpenGlTrace; boolean restrictedBackupMode; @@ -532,6 +532,12 @@ public final class ActivityThread { String pkg; CompatibilityInfo info; } + + static final class RequestActivityExtras { + IBinder activityToken; + IBinder requestToken; + int requestType; + } private native void dumpGraphicsInfo(FileDescriptor fd); @@ -723,9 +729,10 @@ public final class ActivityThread { ComponentName instrumentationName, String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, - int debugMode, boolean enableOpenGlTrace, boolean isRestrictedBackupMode, - boolean persistent, Configuration config, CompatibilityInfo compatInfo, - Map<String, IBinder> services, Bundle coreSettings) { + IUiAutomationConnection instrumentationUiConnection, int debugMode, + boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent, + Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, + Bundle coreSettings) { if (services != null) { // Setup the service cache in the ServiceManager @@ -741,6 +748,7 @@ public final class ActivityThread { data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; + data.instrumentationUiAutomationConnection = instrumentationUiConnection; data.debugMode = debugMode; data.enableOpenGlTrace = enableOpenGlTrace; data.restrictedBackupMode = isRestrictedBackupMode; @@ -1107,6 +1115,16 @@ public final class ActivityThread { queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider); } + @Override + public void requestActivityExtras(IBinder activityToken, IBinder requestToken, + int requestType) { + RequestActivityExtras cmd = new RequestActivityExtras(); + cmd.activityToken = activityToken; + cmd.requestToken = requestToken; + cmd.requestType = requestType; + queueOrSendMessage(H.REQUEST_ACTIVITY_EXTRAS, cmd); + } + private void printRow(PrintWriter pw, String format, Object...objs) { pw.println(String.format(format, objs)); } @@ -1172,6 +1190,7 @@ public final class ActivityThread { public static final int TRIM_MEMORY = 140; public static final int DUMP_PROVIDER = 141; public static final int UNSTABLE_PROVIDER_DIED = 142; + public static final int REQUEST_ACTIVITY_EXTRAS = 143; String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { @@ -1218,6 +1237,7 @@ public final class ActivityThread { case TRIM_MEMORY: return "TRIM_MEMORY"; case DUMP_PROVIDER: return "DUMP_PROVIDER"; case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; + case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS"; } } return Integer.toString(code); @@ -1429,6 +1449,9 @@ public final class ActivityThread { case UNSTABLE_PROVIDER_DIED: handleUnstableProviderDied((IBinder)msg.obj, false); break; + case REQUEST_ACTIVITY_EXTRAS: + handleRequestActivityExtras((RequestActivityExtras)msg.obj); + break; } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } @@ -1564,7 +1587,7 @@ public final class ActivityThread { } public static ActivityThread currentActivityThread() { - return sThreadLocal.get(); + return sCurrentActivityThread; } public static String currentPackageName() { @@ -2321,6 +2344,23 @@ public final class ActivityThread { performNewIntents(data.token, data.intents); } + public void handleRequestActivityExtras(RequestActivityExtras cmd) { + Bundle data = new Bundle(); + ActivityClientRecord r = mActivities.get(cmd.activityToken); + if (r != null) { + r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data); + r.activity.onProvideAssistData(data); + } + if (data.isEmpty()) { + data = null; + } + IActivityManager mgr = ActivityManagerNative.getDefault(); + try { + mgr.reportTopActivityExtras(cmd.requestToken, data); + } catch (RemoteException e) { + } + } + private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>(); /** @@ -4277,7 +4317,7 @@ public final class ActivityThread { // Enable OpenGL tracing if required if (data.enableOpenGlTrace) { - GLUtils.enableTracing(); + GLUtils.setTracingLevel(1); } /** @@ -4336,7 +4376,8 @@ public final class ActivityThread { } mInstrumentation.init(this, instrContext, appContext, - new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher); + new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher, + data.instrumentationUiAutomationConnection); if (mProfiler.profileFile != null && !ii.handleProfiling && mProfiler.profileFd == null) { @@ -4894,7 +4935,7 @@ public final class ActivityThread { } private void attach(boolean system) { - sThreadLocal.set(this); + sCurrentActivityThread = this; mSystemThread = system; if (!system) { ViewRootImpl.addFirstDrawHandler(new Runnable() { diff --git a/core/java/android/util/Pool.java b/core/java/android/app/AppOpsManager.aidl index 8cd4f3e..4b97a15 100644 --- a/core/java/android/util/Pool.java +++ b/core/java/android/app/AppOpsManager.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2009 The Android Open Source Project +/** + * Copyright (c) 2013, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,12 +14,7 @@ * limitations under the License. */ -package android.util; +package android.app; -/** - * @hide - */ -public interface Pool<T extends Poolable<T>> { - public abstract T acquire(); - public abstract void release(T element); -} +parcelable AppOpsManager.PackageOps; +parcelable AppOpsManager.OpEntry; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java new file mode 100644 index 0000000..de69867 --- /dev/null +++ b/core/java/android/app/AppOpsManager.java @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.Manifest; +import com.android.internal.app.IAppOpsService; +import com.android.internal.app.IAppOpsCallback; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.Process; +import android.os.RemoteException; + +/** + * API for interacting with "application operation" tracking. Allows you to: + * + * - Note when operations are happening, and find out if they are allowed for the current caller. + * - Disallow specific apps from doing specific operations. + * - Collect all of the current information about operations that have been executed or are not + * being allowed. + * - Monitor for changes in whether an operation is allowed. + * + * Each operation is identified by a single integer; these integers are a fixed set of + * operations, enumerated by the OP_* constants. + * + * When checking operations, the result is a "mode" integer indicating the current setting + * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but + * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a + * SecurityException back to the caller; the normal operation calls will do this for you). + * + * @hide + */ +public class AppOpsManager { + final Context mContext; + final IAppOpsService mService; + final HashMap<Callback, IAppOpsCallback> mModeWatchers + = new HashMap<Callback, IAppOpsCallback>(); + + public static final int MODE_ALLOWED = 0; + public static final int MODE_IGNORED = 1; + public static final int MODE_ERRORED = 2; + + // when adding one of these: + // - increment _NUM_OP + // - add rows to sOpToSwitch, sOpNames, sOpPerms + // - add descriptive strings to Settings/res/values/arrays.xml + public static final int OP_NONE = -1; + public static final int OP_COARSE_LOCATION = 0; + public static final int OP_FINE_LOCATION = 1; + public static final int OP_GPS = 2; + public static final int OP_VIBRATE = 3; + public static final int OP_READ_CONTACTS = 4; + public static final int OP_WRITE_CONTACTS = 5; + public static final int OP_READ_CALL_LOG = 6; + public static final int OP_WRITE_CALL_LOG = 7; + public static final int OP_READ_CALENDAR = 8; + public static final int OP_WRITE_CALENDAR = 9; + public static final int OP_WIFI_SCAN = 10; + public static final int OP_POST_NOTIFICATION = 11; + public static final int OP_NEIGHBORING_CELLS = 12; + public static final int OP_CALL_PHONE = 13; + public static final int OP_READ_SMS = 14; + public static final int OP_WRITE_SMS = 15; + public static final int OP_RECEIVE_SMS = 16; + public static final int OP_RECEIVE_EMERGECY_SMS = 17; + public static final int OP_RECEIVE_MMS = 18; + public static final int OP_RECEIVE_WAP_PUSH = 19; + public static final int OP_SEND_SMS = 20; + public static final int OP_READ_ICC_SMS = 21; + public static final int OP_WRITE_ICC_SMS = 22; + public static final int OP_WRITE_SETTINGS = 23; + public static final int OP_SYSTEM_ALERT_WINDOW = 24; + public static final int OP_ACCESS_NOTIFICATIONS = 25; + public static final int OP_CAMERA = 26; + public static final int OP_RECORD_AUDIO = 27; + public static final int OP_PLAY_AUDIO = 28; + /** @hide */ + public static final int _NUM_OP = 29; + + /** + * This maps each operation to the operation that serves as the + * switch to determine whether it is allowed. Generally this is + * a 1:1 mapping, but for some things (like location) that have + * multiple low-level operations being tracked that should be + * presented to hte user as one switch then this can be used to + * make them all controlled by the same single operation. + */ + private static int[] sOpToSwitch = new int[] { + OP_COARSE_LOCATION, + OP_COARSE_LOCATION, + OP_COARSE_LOCATION, + OP_VIBRATE, + OP_READ_CONTACTS, + OP_WRITE_CONTACTS, + OP_READ_CALL_LOG, + OP_WRITE_CALL_LOG, + OP_READ_CALENDAR, + OP_WRITE_CALENDAR, + OP_COARSE_LOCATION, + OP_POST_NOTIFICATION, + OP_COARSE_LOCATION, + OP_CALL_PHONE, + OP_READ_SMS, + OP_WRITE_SMS, + OP_READ_SMS, + OP_READ_SMS, + OP_READ_SMS, + OP_READ_SMS, + OP_WRITE_SMS, + OP_READ_SMS, + OP_WRITE_SMS, + OP_WRITE_SETTINGS, + OP_SYSTEM_ALERT_WINDOW, + OP_ACCESS_NOTIFICATIONS, + OP_CAMERA, + OP_RECORD_AUDIO, + OP_PLAY_AUDIO, + }; + + /** + * This provides a simple name for each operation to be used + * in debug output. + */ + private static String[] sOpNames = new String[] { + "COARSE_LOCATION", + "FINE_LOCATION", + "GPS", + "VIBRATE", + "READ_CONTACTS", + "WRITE_CONTACTS", + "READ_CALL_LOG", + "WRITE_CALL_LOG", + "READ_CALENDAR", + "WRITE_CALENDAR", + "WIFI_SCAN", + "POST_NOTIFICATION", + "NEIGHBORING_CELLS", + "CALL_PHONE", + "READ_SMS", + "WRITE_SMS", + "RECEIVE_SMS", + "RECEIVE_EMERGECY_SMS", + "RECEIVE_MMS", + "RECEIVE_WAP_PUSH", + "SEND_SMS", + "READ_ICC_SMS", + "WRITE_ICC_SMS", + "WRITE_SETTINGS", + "SYSTEM_ALERT_WINDOW", + "ACCESS_NOTIFICATIONS", + "CAMERA", + "RECORD_AUDIO", + "PLAY_AUDIO", + }; + + /** + * This optionally maps a permission to an operation. If there + * is no permission associated with an operation, it is null. + */ + private static String[] sOpPerms = new String[] { + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION, + null, + android.Manifest.permission.VIBRATE, + android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.READ_CALL_LOG, + android.Manifest.permission.WRITE_CALL_LOG, + android.Manifest.permission.READ_CALENDAR, + android.Manifest.permission.WRITE_CALENDAR, + null, // no permission required for notifications + android.Manifest.permission.ACCESS_WIFI_STATE, + null, // neighboring cells shares the coarse location perm + android.Manifest.permission.CALL_PHONE, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.WRITE_SMS, + android.Manifest.permission.RECEIVE_SMS, + android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST, + android.Manifest.permission.RECEIVE_MMS, + android.Manifest.permission.RECEIVE_WAP_PUSH, + android.Manifest.permission.SEND_SMS, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.WRITE_SMS, + android.Manifest.permission.WRITE_SETTINGS, + android.Manifest.permission.SYSTEM_ALERT_WINDOW, + android.Manifest.permission.ACCESS_NOTIFICATIONS, + android.Manifest.permission.CAMERA, + android.Manifest.permission.RECORD_AUDIO, + null, // no permission for playing audio + }; + + /** + * Retrieve the op switch that controls the given operation. + */ + public static int opToSwitch(int op) { + return sOpToSwitch[op]; + } + + /** + * Retrieve a non-localized name for the operation, for debugging output. + */ + public static String opToName(int op) { + if (op == OP_NONE) return "NONE"; + return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")"); + } + + /** + * Retrieve the permission associated with an operation, or null if there is not one. + */ + public static String opToPermission(int op) { + return sOpPerms[op]; + } + + /** + * Class holding all of the operation information associated with an app. + */ + public static class PackageOps implements Parcelable { + private final String mPackageName; + private final int mUid; + private final List<OpEntry> mEntries; + + public PackageOps(String packageName, int uid, List<OpEntry> entries) { + mPackageName = packageName; + mUid = uid; + mEntries = entries; + } + + public String getPackageName() { + return mPackageName; + } + + public int getUid() { + return mUid; + } + + public List<OpEntry> getOps() { + return mEntries; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mPackageName); + dest.writeInt(mUid); + dest.writeInt(mEntries.size()); + for (int i=0; i<mEntries.size(); i++) { + mEntries.get(i).writeToParcel(dest, flags); + } + } + + PackageOps(Parcel source) { + mPackageName = source.readString(); + mUid = source.readInt(); + mEntries = new ArrayList<OpEntry>(); + final int N = source.readInt(); + for (int i=0; i<N; i++) { + mEntries.add(OpEntry.CREATOR.createFromParcel(source)); + } + } + + public static final Creator<PackageOps> CREATOR = new Creator<PackageOps>() { + @Override public PackageOps createFromParcel(Parcel source) { + return new PackageOps(source); + } + + @Override public PackageOps[] newArray(int size) { + return new PackageOps[size]; + } + }; + } + + /** + * Class holding the information about one unique operation of an application. + */ + public static class OpEntry implements Parcelable { + private final int mOp; + private final int mMode; + private final long mTime; + private final long mRejectTime; + private final int mDuration; + + public OpEntry(int op, int mode, long time, long rejectTime, int duration) { + mOp = op; + mMode = mode; + mTime = time; + mRejectTime = rejectTime; + mDuration = duration; + } + + public int getOp() { + return mOp; + } + + public int getMode() { + return mMode; + } + + public long getTime() { + return mTime; + } + + public long getRejectTime() { + return mRejectTime; + } + + public boolean isRunning() { + return mDuration == -1; + } + + public int getDuration() { + return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mOp); + dest.writeInt(mMode); + dest.writeLong(mTime); + dest.writeLong(mRejectTime); + dest.writeInt(mDuration); + } + + OpEntry(Parcel source) { + mOp = source.readInt(); + mMode = source.readInt(); + mTime = source.readLong(); + mRejectTime = source.readLong(); + mDuration = source.readInt(); + } + + public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() { + @Override public OpEntry createFromParcel(Parcel source) { + return new OpEntry(source); + } + + @Override public OpEntry[] newArray(int size) { + return new OpEntry[size]; + } + }; + } + + /** + * Callback for notification of changes to operation state. + */ + public interface Callback { + public void opChanged(int op, String packageName); + } + + public AppOpsManager(Context context, IAppOpsService service) { + mContext = context; + mService = service; + } + + /** + * Retrieve current operation state for all applications. + * + * @param ops The set of operations you are interested in, or null if you want all of them. + */ + public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) { + try { + return mService.getPackagesForOps(ops); + } catch (RemoteException e) { + } + return null; + } + + /** + * Retrieve current operation state for one application. + * + * @param uid The uid of the application of interest. + * @param packageName The name of the application of interest. + * @param ops The set of operations you are interested in, or null if you want all of them. + */ + public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) { + try { + return mService.getOpsForPackage(uid, packageName, ops); + } catch (RemoteException e) { + } + return null; + } + + public void setMode(int code, int uid, String packageName, int mode) { + try { + mService.setMode(code, uid, packageName, mode); + } catch (RemoteException e) { + } + } + + public void startWatchingMode(int op, String packageName, final Callback callback) { + synchronized (mModeWatchers) { + IAppOpsCallback cb = mModeWatchers.get(callback); + if (cb == null) { + cb = new IAppOpsCallback.Stub() { + public void opChanged(int op, String packageName) { + callback.opChanged(op, packageName); + } + }; + mModeWatchers.put(callback, cb); + } + try { + mService.startWatchingMode(op, packageName, cb); + } catch (RemoteException e) { + } + } + } + + public void stopWatchingMode(Callback callback) { + synchronized (mModeWatchers) { + IAppOpsCallback cb = mModeWatchers.get(callback); + if (cb != null) { + try { + mService.stopWatchingMode(cb); + } catch (RemoteException e) { + } + } + } + } + + public int checkOp(int op, int uid, String packageName) { + try { + int mode = mService.checkOperation(op, uid, packageName); + if (mode == MODE_ERRORED) { + throw new SecurityException("Operation not allowed"); + } + return mode; + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + public int checkOpNoThrow(int op, int uid, String packageName) { + try { + return mService.checkOperation(op, uid, packageName); + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + public int noteOp(int op, int uid, String packageName) { + try { + int mode = mService.noteOperation(op, uid, packageName); + if (mode == MODE_ERRORED) { + throw new SecurityException("Operation not allowed"); + } + return mode; + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + public int noteOpNoThrow(int op, int uid, String packageName) { + try { + return mService.noteOperation(op, uid, packageName); + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + public int noteOp(int op) { + return noteOp(op, Process.myUid(), mContext.getBasePackageName()); + } + + public int startOp(int op, int uid, String packageName) { + try { + int mode = mService.startOperation(op, uid, packageName); + if (mode == MODE_ERRORED) { + throw new SecurityException("Operation not allowed"); + } + return mode; + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + public int startOpNoThrow(int op, int uid, String packageName) { + try { + return mService.startOperation(op, uid, packageName); + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + public int startOp(int op) { + return startOp(op, Process.myUid(), mContext.getBasePackageName()); + } + + public void finishOp(int op, int uid, String packageName) { + try { + mService.finishOperation(op, uid, packageName); + } catch (RemoteException e) { + } + } + + public void finishOp(int op) { + finishOp(op, Process.myUid(), mContext.getBasePackageName()); + } +} diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 3a67cec..132388e 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -22,6 +22,7 @@ import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; @@ -45,6 +46,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { new ArrayList<ComponentCallbacks>(); private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = new ArrayList<ActivityLifecycleCallbacks>(); + private ArrayList<OnProvideAssistData> mAssistCallbacks = null; /** @hide */ public LoadedApk mLoadedApk; @@ -59,6 +61,21 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { void onActivityDestroyed(Activity activity); } + /** + * Callback interface for use with {@link Application#registerOnProvideAssistData} + * and {@link Application#unregisterOnProvideAssistData}. + */ + public interface OnProvideAssistData { + /** + * This is called when the user is requesting an assist, to build a full + * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current + * application. You can override this method to place into the bundle anything + * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part + * of the assist Intent. + */ + public void onProvideAssistData(Activity activity, Bundle data); + } + public Application() { super(null); } @@ -137,7 +154,24 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { mActivityLifecycleCallbacks.remove(callback); } } - + + public void registerOnProvideAssistData(OnProvideAssistData callback) { + synchronized (this) { + if (mAssistCallbacks == null) { + mAssistCallbacks = new ArrayList<OnProvideAssistData>(); + } + mAssistCallbacks.add(callback); + } + } + + public void unregisterOnProvideAssistData(OnProvideAssistData callback) { + synchronized (this) { + if (mAssistCallbacks != null) { + mAssistCallbacks.remove(callback); + } + } + } + // ------------------ Internal API ------------------ /** @@ -232,4 +266,19 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } return callbacks; } + + /* package */ void dispatchOnProvideAssistData(Activity activity, Bundle data) { + Object[] callbacks; + synchronized (this) { + if (mAssistCallbacks == null) { + return; + } + callbacks = mAssistCallbacks.toArray(); + } + if (callbacks != null) { + for (int i=0; i<callbacks.length; i++) { + ((OnProvideAssistData)callbacks[i]).onProvideAssistData(activity, data); + } + } + } } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7431765..f09c2fe 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -142,6 +142,21 @@ final class ApplicationPackageManager extends PackageManager { } @Override + public int getPackageUid(String packageName, int userHandle) + throws NameNotFoundException { + try { + int uid = mPM.getPackageUid(packageName, userHandle); + if (uid >= 0) { + return uid; + } + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + + throw new NameNotFoundException(packageName); + } + + @Override public PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException { try { @@ -411,17 +426,22 @@ final class ApplicationPackageManager extends PackageManager { @Override public List<PackageInfo> getInstalledPackages(int flags, int userId) { try { - final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>(); - PackageInfo lastItem = null; - ParceledListSlice<PackageInfo> slice; - - do { - final String lastKey = lastItem != null ? lastItem.packageName : null; - slice = mPM.getInstalledPackages(flags, lastKey, userId); - lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR); - } while (!slice.isLastSlice()); + ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId); + return slice.getList(); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } - return packageInfos; + @SuppressWarnings("unchecked") + @Override + public List<PackageInfo> getPackagesHoldingPermissions( + String[] permissions, int flags) { + final int userId = mContext.getUserId(); + try { + ParceledListSlice<PackageInfo> slice = mPM.getPackagesHoldingPermissions( + permissions, flags, userId); + return slice.getList(); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } @@ -432,17 +452,8 @@ final class ApplicationPackageManager extends PackageManager { public List<ApplicationInfo> getInstalledApplications(int flags) { final int userId = mContext.getUserId(); try { - final List<ApplicationInfo> applicationInfos = new ArrayList<ApplicationInfo>(); - ApplicationInfo lastItem = null; - ParceledListSlice<ApplicationInfo> slice; - - do { - final String lastKey = lastItem != null ? lastItem.packageName : null; - slice = mPM.getInstalledApplications(flags, lastKey, userId); - lastItem = slice.populateList(applicationInfos, ApplicationInfo.CREATOR); - } while (!slice.isLastSlice()); - - return applicationInfos; + ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId); + return slice.getList(); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 63aa5f9..b1c58f2 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -267,6 +267,9 @@ public abstract class ApplicationThreadNative extends Binder Bundle testArgs = data.readBundle(); IBinder binder = data.readStrongBinder(); IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder); + binder = data.readStrongBinder(); + IUiAutomationConnection uiAutomationConnection = + IUiAutomationConnection.Stub.asInterface(binder); int testMode = data.readInt(); boolean openGlTrace = data.readInt() != 0; boolean restrictedBackupMode = (data.readInt() != 0); @@ -277,8 +280,9 @@ public abstract class ApplicationThreadNative extends Binder Bundle coreSettings = data.readBundle(); bindApplication(packageName, info, providers, testName, profileName, profileFd, autoStopProfiler, - testArgs, testWatcher, testMode, openGlTrace, restrictedBackupMode, - persistent, config, compatInfo, services, coreSettings); + testArgs, testWatcher, uiAutomationConnection, testMode, + openGlTrace, restrictedBackupMode, persistent, config, compatInfo, + services, coreSettings); return true; } @@ -587,6 +591,17 @@ public abstract class ApplicationThreadNative extends Binder reply.writeNoException(); return true; } + + case REQUEST_ACTIVITY_EXTRAS_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + IBinder activityToken = data.readStrongBinder(); + IBinder requestToken = data.readStrongBinder(); + int requestType = data.readInt(); + requestActivityExtras(activityToken, requestToken, requestType); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -863,10 +878,11 @@ class ApplicationThreadProxy implements IApplicationThread { public final void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers, ComponentName testName, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle testArgs, - IInstrumentationWatcher testWatcher, int debugMode, boolean openGlTrace, - boolean restrictedBackupMode, boolean persistent, - Configuration config, CompatibilityInfo compatInfo, - Map<String, IBinder> services, Bundle coreSettings) throws RemoteException { + IInstrumentationWatcher testWatcher, + IUiAutomationConnection uiAutomationConnection, int debugMode, + boolean openGlTrace, boolean restrictedBackupMode, boolean persistent, + Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, + Bundle coreSettings) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeString(packageName); @@ -888,6 +904,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(autoStopProfiler ? 1 : 0); data.writeBundle(testArgs); data.writeStrongInterface(testWatcher); + data.writeStrongInterface(uiAutomationConnection); data.writeInt(debugMode); data.writeInt(openGlTrace ? 1 : 0); data.writeInt(restrictedBackupMode ? 1 : 0); @@ -1185,4 +1202,15 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } + + public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType) + throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeStrongBinder(activityToken); + data.writeStrongBinder(requestToken); + data.writeInt(requestType); + mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f895ccc..734d435 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -47,11 +47,9 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.hardware.ISerialManager; -import android.hardware.SensorManager; import android.hardware.SerialManager; import android.hardware.SystemSensorManager; import android.hardware.display.DisplayManager; -import android.hardware.input.IInputManager; import android.hardware.input.InputManager; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbManager; @@ -65,8 +63,6 @@ import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkPolicyManager; import android.net.NetworkPolicyManager; -import android.net.ThrottleManager; -import android.net.IThrottleManager; import android.net.Uri; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; @@ -109,6 +105,8 @@ import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; + +import com.android.internal.app.IAppOpsService; import com.android.internal.os.IDropBoxManagerService; import java.io.File; @@ -462,7 +460,8 @@ class ContextImpl extends Context { registerService(STORAGE_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { try { - return new StorageManager(ctx.mMainThread.getHandler().getLooper()); + return new StorageManager( + ctx.getContentResolver(), ctx.mMainThread.getHandler().getLooper()); } catch (RemoteException rex) { Log.e(TAG, "Failed to create StorageManager", rex); return null; @@ -474,12 +473,6 @@ class ContextImpl extends Context { return new TelephonyManager(ctx.getOuterContext()); }}); - registerService(THROTTLE_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { - IBinder b = ServiceManager.getService(THROTTLE_SERVICE); - return new ThrottleManager(IThrottleManager.Stub.asInterface(b)); - }}); - registerService(UI_MODE_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new UiModeManager(); @@ -499,7 +492,7 @@ class ContextImpl extends Context { registerService(VIBRATOR_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { - return new SystemVibrator(); + return new SystemVibrator(ctx); }}); registerService(WALLPAPER_SERVICE, WALLPAPER_FETCHER); @@ -530,11 +523,18 @@ class ContextImpl extends Context { }}); registerService(USER_SERVICE, new ServiceFetcher() { - public Object getService(ContextImpl ctx) { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(USER_SERVICE); IUserManager service = IUserManager.Stub.asInterface(b); return new UserManager(ctx, service); }}); + + registerService(APP_OPS_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(APP_OPS_SERVICE); + IAppOpsService service = IAppOpsService.Stub.asInterface(b); + return new AppOpsManager(ctx, service); + }}); } static ContextImpl getImpl(Context context) { @@ -624,7 +624,15 @@ class ContextImpl extends Context { if (mPackageInfo != null) { return mPackageInfo.getPackageName(); } - throw new RuntimeException("Not supported in system context"); + // No mPackageInfo means this is a Context for the system itself, + // and this here is its name. + return "android"; + } + + /** @hide */ + @Override + public String getBasePackageName() { + return mBasePackageName != null ? mBasePackageName : getPackageName(); } @Override @@ -956,7 +964,7 @@ class ContextImpl extends Context { public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { try { ActivityManagerNative.getDefault().startActivityAsUser( - mMainThread.getApplicationThread(), intent, + mMainThread.getApplicationThread(), getBasePackageName(), intent, intent.resolveTypeIfNeeded(getContentResolver()), null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, options, user.getIdentifier()); @@ -1035,7 +1043,7 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, false, false, + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, getUserId()); } catch (RemoteException e) { } @@ -1049,7 +1057,21 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, false, false, + Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, + false, false, getUserId()); + } catch (RemoteException e) { + } + } + + @Override + public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { + warnIfCallingFromSystemProcess(); + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.setAllowFds(false); + ActivityManagerNative.getDefault().broadcastIntent( + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false, getUserId()); } catch (RemoteException e) { } @@ -1064,7 +1086,7 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, true, false, + Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false, getUserId()); } catch (RemoteException e) { } @@ -1075,6 +1097,15 @@ class ContextImpl extends Context { String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE, + resultReceiver, scheduler, initialCode, initialData, initialExtras); + } + + @Override + public void sendOrderedBroadcast(Intent intent, + String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { warnIfCallingFromSystemProcess(); IIntentReceiver rd = null; if (resultReceiver != null) { @@ -1098,8 +1129,8 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, - initialCode, initialData, initialExtras, receiverPermission, - true, false, getUserId()); + initialCode, initialData, initialExtras, receiverPermission, appOp, + true, false, getUserId()); } catch (RemoteException e) { } } @@ -1110,8 +1141,8 @@ class ContextImpl extends Context { try { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), - intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false, - user.getIdentifier()); + intent, resolvedType, null, Activity.RESULT_OK, null, null, null, + AppOpsManager.OP_NONE, false, false, user.getIdentifier()); } catch (RemoteException e) { } } @@ -1124,7 +1155,7 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, false, false, + Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, false, false, user.getIdentifier()); } catch (RemoteException e) { } @@ -1157,7 +1188,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, - true, false, user.getIdentifier()); + AppOpsManager.OP_NONE, true, false, user.getIdentifier()); } catch (RemoteException e) { } } @@ -1170,7 +1201,7 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, false, true, + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, getUserId()); } catch (RemoteException e) { } @@ -1205,7 +1236,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - true, true, getUserId()); + AppOpsManager.OP_NONE, true, true, getUserId()); } catch (RemoteException e) { } } @@ -1232,7 +1263,7 @@ class ContextImpl extends Context { intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, false, true, user.getIdentifier()); + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier()); } catch (RemoteException e) { } } @@ -1265,7 +1296,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - true, true, user.getIdentifier()); + AppOpsManager.OP_NONE, true, true, user.getIdentifier()); } catch (RemoteException e) { } } @@ -1404,12 +1435,13 @@ class ContextImpl extends Context { public boolean bindService(Intent service, ServiceConnection conn, int flags) { warnIfCallingFromSystemProcess(); - return bindService(service, conn, flags, UserHandle.getUserId(Process.myUid())); + return bindServiceAsUser(service, conn, flags, Process.myUserHandle()); } /** @hide */ @Override - public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) { + public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, + UserHandle user) { IServiceConnection sd; if (conn == null) { throw new IllegalArgumentException("connection is null"); @@ -1431,7 +1463,7 @@ class ContextImpl extends Context { int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), - sd, flags, userHandle); + sd, flags, user.getIdentifier()); if (res < 0) { throw new SecurityException( "Not allowed to bind to service " + service); @@ -1467,7 +1499,7 @@ class ContextImpl extends Context { arguments.setAllowFds(false); } return ActivityManagerNative.getDefault().startInstrumentation( - className, profileFile, 0, arguments, null, getUserId()); + className, profileFile, 0, arguments, null, null, getUserId()); } catch (RemoteException e) { // System has crashed, nothing we can do. } diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 32e40ee..26dc60d 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -1085,6 +1085,7 @@ public class DownloadManager { values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1); values.putNull(Downloads.Impl._DATA); values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING); + values.put(Downloads.Impl.COLUMN_FAILED_CONNECTIONS, 0); mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8af17a4..cf4c729 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -51,19 +51,19 @@ import java.util.List; * {@hide} */ public interface IActivityManager extends IInterface { - public int startActivity(IApplicationThread caller, + public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) throws RemoteException; - public int startActivityAsUser(IApplicationThread caller, + public int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException; - public WaitResult startActivityAndWait(IApplicationThread caller, + public WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException; - public int startActivityWithConfig(IApplicationThread caller, + public int startActivityWithConfig(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Configuration newConfig, Bundle options, int userId) throws RemoteException; @@ -85,7 +85,7 @@ public interface IActivityManager extends IInterface { public int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, - boolean serialized, boolean sticky, int userId) throws RemoteException; + int appOp, boolean serialized, boolean sticky, int userId) throws RemoteException; public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException; public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException; public void attachApplication(IApplicationThread app) throws RemoteException; @@ -158,8 +158,8 @@ public interface IActivityManager extends IInterface { public void killApplicationProcess(String processName, int uid) throws RemoteException; public boolean startInstrumentation(ComponentName className, String profileFile, - int flags, Bundle arguments, IInstrumentationWatcher watcher, int userId) - throws RemoteException; + int flags, Bundle arguments, IInstrumentationWatcher watcher, + IUiAutomationConnection connection, int userId) throws RemoteException; public void finishInstrumentation(IApplicationThread target, int resultCode, Bundle results) throws RemoteException; @@ -310,7 +310,7 @@ public interface IActivityManager extends IInterface { public boolean dumpHeap(String process, int userId, boolean managed, String path, ParcelFileDescriptor fd) throws RemoteException; - public int startActivities(IApplicationThread caller, + public int startActivities(IApplicationThread caller, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options, int userId) throws RemoteException; @@ -357,9 +357,10 @@ public interface IActivityManager extends IInterface { public boolean navigateUpTo(IBinder token, Intent target, int resultCode, Intent resultData) throws RemoteException; - // This is not public because you need to be very careful in how you + // These are not public because you need to be very careful in how you // manage your activity to make sure it is always the uid you expect. public int getLaunchedFromUid(IBinder activityToken) throws RemoteException; + public String getLaunchedFromPackage(IBinder activityToken) throws RemoteException; public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; @@ -368,6 +369,10 @@ public interface IActivityManager extends IInterface { public long inputDispatchingTimedOut(int pid, boolean aboveSystem) throws RemoteException; + public Bundle getTopActivityExtras(int requestType) throws RemoteException; + + public void reportTopActivityExtras(IBinder token, Bundle extras) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -624,4 +629,7 @@ public interface IActivityManager extends IInterface { int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158; int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159; int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160; + int GET_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+161; + int REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+162; + int GET_LAUNCHED_FROM_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+163; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 03a26d4..3189b31 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -90,7 +90,8 @@ public interface IApplicationThread extends IInterface { void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers, ComponentName testName, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle testArguments, IInstrumentationWatcher testWatcher, - int debugMode, boolean openGlTrace, boolean restrictedBackupMode, boolean persistent, + IUiAutomationConnection uiAutomationConnection, int debugMode, + boolean openGlTrace, boolean restrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) throws RemoteException; void scheduleExit() throws RemoteException; @@ -130,6 +131,8 @@ public interface IApplicationThread extends IInterface { void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException; void unstableProviderDied(IBinder provider) throws RemoteException; + void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType) + throws RemoteException; String descriptor = "android.app.IApplicationThread"; @@ -179,4 +182,5 @@ public interface IApplicationThread extends IInterface { int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44; int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45; int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46; + int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47; } diff --git a/core/java/android/net/IThrottleManager.aidl b/core/java/android/app/INotificationListener.aidl index a12469d..f010a2a 100644 --- a/core/java/android/net/IThrottleManager.aidl +++ b/core/java/android/app/INotificationListener.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010, The Android Open Source Project + * Copyright (c) 2013, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,27 +14,13 @@ * limitations under the License. */ -package android.net; +package android.app; -import android.os.IBinder; +import com.android.internal.statusbar.StatusBarNotification; -/** - * Interface that answers queries about data transfer amounts and throttling - */ -/** {@hide} */ -interface IThrottleManager +/** @hide */ +oneway interface INotificationListener { - long getByteCount(String iface, int dir, int period, int ago); - - int getThrottle(String iface); - - long getResetTime(String iface); - - long getPeriodStartTime(String iface); - - long getCliffThreshold(String iface, int cliff); - - int getCliffLevel(String iface, int cliff); - - String getHelpUri(); -} + void onNotificationPosted(in StatusBarNotification notification); + void onNotificationRemoved(in StatusBarNotification notification); +}
\ No newline at end of file diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 62d4962..14bcc0d 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -17,10 +17,13 @@ package android.app; +import android.app.INotificationListener; import android.app.ITransientNotification; import android.app.Notification; import android.content.Intent; +import com.android.internal.statusbar.StatusBarNotification; + /** {@hide} */ interface INotificationManager { @@ -28,11 +31,17 @@ interface INotificationManager void enqueueToast(String pkg, ITransientNotification callback, int duration); void cancelToast(String pkg, ITransientNotification callback); - void enqueueNotificationWithTag(String pkg, String tag, int id, + void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id, in Notification notification, inout int[] idReceived, int userId); void cancelNotificationWithTag(String pkg, String tag, int id, int userId); - void setNotificationsEnabledForPackage(String pkg, boolean enabled); - boolean areNotificationsEnabledForPackage(String pkg); + void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled); + boolean areNotificationsEnabledForPackage(String pkg, int uid); + + StatusBarNotification[] getActiveNotifications(String callingPkg); + StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count); + + void registerListener(in INotificationListener listener, int userid); + void unregisterListener(in INotificationListener listener, int userid); } diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl new file mode 100644 index 0000000..09bf829 --- /dev/null +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.accessibilityservice.IAccessibilityServiceClient; +import android.graphics.Bitmap; +import android.view.InputEvent; +import android.os.ParcelFileDescriptor; + +/** + * This interface contains privileged operations a shell program can perform + * on behalf of an instrumentation that it runs. These operations require + * special permissions which the shell user has but the instrumentation does + * not. Running privileged operations by the shell user on behalf of an + * instrumentation is needed for running UiTestCases. + * + * {@hide} + */ +interface IUiAutomationConnection { + void connect(IAccessibilityServiceClient client); + void disconnect(); + boolean injectInputEvent(in InputEvent event, boolean sync); + boolean setRotation(int rotation); + Bitmap takeScreenshot(int width, int height); + void shutdown(); +} diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index e0856ae..e7bf305 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -27,6 +27,7 @@ import android.hardware.input.InputManager; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; +import android.os.Looper; import android.os.MessageQueue; import android.os.PerformanceCollector; import android.os.Process; @@ -48,7 +49,6 @@ import java.io.File; import java.util.ArrayList; import java.util.List; - /** * Base class for implementing application instrumentation code. When running * with instrumentation turned on, this class will be instantiated for you @@ -58,6 +58,7 @@ import java.util.List; * <instrumentation> tag. */ public class Instrumentation { + /** * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * identifies the class that is writing the report. This can be used to provide more structured @@ -72,7 +73,7 @@ public class Instrumentation { * instrumentation can also be launched, and results collected, by an automated system. */ public static final String REPORT_KEY_STREAMRESULT = "stream"; - + private static final String TAG = "Instrumentation"; private final Object mSync = new Object(); @@ -85,9 +86,11 @@ public class Instrumentation { private List<ActivityWaiter> mWaitingActivities; private List<ActivityMonitor> mActivityMonitors; private IInstrumentationWatcher mWatcher; + private IUiAutomationConnection mUiAutomationConnection; private boolean mAutomaticPerformanceSnapshots = false; private PerformanceCollector mPerformanceCollector; private Bundle mPerfMetrics = new Bundle(); + private UiAutomation mUiAutomation; public Instrumentation() { } @@ -1410,7 +1413,7 @@ public class Instrumentation { intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); int result = ActivityManagerNative.getDefault() - .startActivity(whoThread, intent, + .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, null, options); @@ -1468,15 +1471,16 @@ public class Instrumentation { resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); } int result = ActivityManagerNative.getDefault() - .startActivities(whoThread, intents, resolvedTypes, token, options, - userId); + .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes, + token, options, userId); checkStartActivityResult(result, intents[0]); } catch (RemoteException e) { } } /** - * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)}, + * Like {@link #execStartActivity(android.content.Context, android.os.IBinder, + * android.os.IBinder, Fragment, android.content.Intent, int, android.os.Bundle)}, * but for calls from a {#link Fragment}. * * @param who The Context from which the activity is being started. @@ -1525,7 +1529,7 @@ public class Instrumentation { intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); int result = ActivityManagerNative.getDefault() - .startActivity(whoThread, intent, + .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mWho : null, requestCode, 0, null, null, options); @@ -1585,7 +1589,7 @@ public class Instrumentation { intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); int result = ActivityManagerNative.getDefault() - .startActivityAsUser(whoThread, intent, + .startActivityAsUser(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, null, options, user.getIdentifier()); @@ -1597,13 +1601,14 @@ public class Instrumentation { /*package*/ final void init(ActivityThread thread, Context instrContext, Context appContext, ComponentName component, - IInstrumentationWatcher watcher) { + IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) { mThread = thread; mMessageQueue = mThread.getLooper().myQueue(); mInstrContext = instrContext; mAppContext = appContext; mComponent = component; mWatcher = watcher; + mUiAutomationConnection = uiAutomationConnection; } /*package*/ static void checkStartActivityResult(int res, Object intent) { @@ -1637,18 +1642,48 @@ public class Instrumentation { } private final void validateNotAppThread() { - if (ActivityThread.currentActivityThread() != null) { + if (Looper.myLooper() == Looper.getMainLooper()) { throw new RuntimeException( "This method can not be called from the main application thread"); } } + /** + * Gets the {@link UiAutomation} instance. + * <p> + * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation} + * work across application boundaries while the APIs exposed by the instrumentation + * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will + * not allow you to inject the event in an app different from the instrumentation + * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)} + * will work regardless of the current application. + * </p> + * <p> + * A typical test case should be using either the {@link UiAutomation} or + * {@link Instrumentation} APIs. Using both APIs at the same time is not + * a mistake by itself but a client has to be aware of the APIs limitations. + * </p> + * @return The UI automation instance. + * + * @see UiAutomation + */ + public UiAutomation getUiAutomation() { + if (mUiAutomationConnection != null) { + if (mUiAutomation == null) { + mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(), + mUiAutomationConnection); + mUiAutomation.connect(); + } + return mUiAutomation; + } + return null; + } + private final class InstrumentationThread extends Thread { public InstrumentationThread(String name) { super(name); } public void run() { - IActivityManager am = ActivityManagerNative.getDefault(); try { Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); } catch (RuntimeException e) { @@ -1659,9 +1694,13 @@ public class Instrumentation { startPerformanceSnapshot(); } onStart(); + if (mUiAutomation != null) { + mUiAutomation.disconnect(); + mUiAutomation = null; + } } } - + private static final class EmptyRunnable implements Runnable { public void run() { } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 3f8e16c..ebca041 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -25,15 +25,11 @@ import android.graphics.Bitmap; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; -import android.util.IntProperty; -import android.util.Log; -import android.util.Slog; import android.util.TypedValue; import android.view.View; import android.widget.ProgressBar; @@ -436,14 +432,23 @@ public class Notification implements Parcelable * @hide */ public static final String EXTRA_PEOPLE = "android.people"; - - private Bundle extras; + /** @hide */ + public static final String EXTRA_TITLE = "android.title"; + /** @hide */ + public static final String EXTRA_TEXT = "android.text"; + /** @hide */ + public static final String EXTRA_SUBTEXT = "android.subtext"; + /** @hide */ + public static final String EXTRA_SMALL_ICON = "android.icon"; + + /** @hide */ + public Bundle extras = new Bundle(); /** * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification. * @hide */ - private static class Action implements Parcelable { + public static class Action implements Parcelable { public int icon; public CharSequence title; public PendingIntent actionIntent; @@ -495,7 +500,10 @@ public class Notification implements Parcelable }; } - private Action[] actions; + /** + * @hide + */ + public Action[] actions; /** * Constructs a Notification object with default values. @@ -589,11 +597,10 @@ public class Notification implements Parcelable kind = parcel.createStringArray(); // may set kind to null - if (parcel.readInt() != 0) { - extras = parcel.readBundle(); - } + extras = parcel.readBundle(); // may be null + + actions = parcel.createTypedArray(Action.CREATOR); // may be null - actions = parcel.createTypedArray(Action.CREATOR); if (parcel.readInt() != 0) { bigContentView = RemoteViews.CREATOR.createFromParcel(parcel); } @@ -602,7 +609,11 @@ public class Notification implements Parcelable @Override public Notification clone() { Notification that = new Notification(); + cloneInto(that); + return that; + } + private void cloneInto(Notification that) { that.when = this.when; that.icon = this.icon; that.number = this.number; @@ -656,15 +667,16 @@ public class Notification implements Parcelable } - that.actions = new Action[this.actions.length]; - for(int i=0; i<this.actions.length; i++) { - that.actions[i] = this.actions[i].clone(); + if (this.actions != null) { + that.actions = new Action[this.actions.length]; + for(int i=0; i<this.actions.length; i++) { + that.actions[i] = this.actions[i].clone(); + } } + if (this.bigContentView != null) { that.bigContentView = this.bigContentView.clone(); } - - return that; } public int describeContents() { @@ -745,14 +757,9 @@ public class Notification implements Parcelable parcel.writeStringArray(kind); // ok for null - if (extras != null) { - parcel.writeInt(1); - extras.writeToParcel(parcel, 0); - } else { - parcel.writeInt(0); - } + parcel.writeBundle(extras); // null ok - parcel.writeTypedArray(actions, 0); + parcel.writeTypedArray(actions, 0); // null ok if (bigContentView != null) { parcel.writeInt(1); @@ -800,35 +807,29 @@ public class Notification implements Parcelable @Deprecated public void setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { - // TODO: rewrite this to use Builder - RemoteViews contentView = new RemoteViews(context.getPackageName(), - R.layout.notification_template_base); - if (this.icon != 0) { - contentView.setImageViewResource(R.id.icon, this.icon); - } - if (priority < PRIORITY_LOW) { - contentView.setInt(R.id.icon, - "setBackgroundResource", R.drawable.notification_template_icon_low_bg); - contentView.setInt(R.id.status_bar_latest_event_content, - "setBackgroundResource", R.drawable.notification_bg_low); - } + Notification.Builder builder = new Notification.Builder(context); + + // First, ensure that key pieces of information that may have been set directly + // are preserved + builder.setWhen(this.when); + builder.setSmallIcon(this.icon); + builder.setPriority(this.priority); + builder.setTicker(this.tickerText); + builder.setNumber(this.number); + builder.mFlags = this.flags; + builder.setSound(this.sound, this.audioStreamType); + builder.setDefaults(this.defaults); + builder.setVibrate(this.vibrate); + + // now apply the latestEventInfo fields if (contentTitle != null) { - contentView.setTextViewText(R.id.title, contentTitle); + builder.setContentTitle(contentTitle); } if (contentText != null) { - contentView.setTextViewText(R.id.text, contentText); + builder.setContentText(contentText); } - if (this.when != 0) { - contentView.setViewVisibility(R.id.time, View.VISIBLE); - contentView.setLong(R.id.time, "setTime", when); - } - if (this.number != 0) { - NumberFormat f = NumberFormat.getIntegerInstance(); - contentView.setTextViewText(R.id.info, f.format(this.number)); - } - - this.contentView = contentView; - this.contentIntent = contentIntent; + builder.setContentIntent(contentIntent); + builder.buildInto(this); } @Override @@ -1615,11 +1616,20 @@ public class Notification implements Parcelable n.kind = null; } n.priority = mPriority; - n.extras = mExtras != null ? new Bundle(mExtras) : null; if (mActions.size() > 0) { n.actions = new Action[mActions.size()]; mActions.toArray(n.actions); } + + n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle(); + + // Store original information used in the construction of this object + n.extras.putCharSequence(EXTRA_TITLE, mContentTitle); + n.extras.putCharSequence(EXTRA_TEXT, mContentText); + n.extras.putCharSequence(EXTRA_SUBTEXT, mSubText); + n.extras.putInt(EXTRA_SMALL_ICON, mSmallIcon); + //n.extras.putByteArray(EXTRA_LARGE_ICON, ... + return n; } @@ -1642,6 +1652,16 @@ public class Notification implements Parcelable return buildUnstyled(); } } + + /** + * Apply this Builder to an existing {@link Notification} object. + * + * @hide + */ + public Notification buildInto(Notification n) { + build().cloneInto(n); + return n; + } } @@ -1882,6 +1902,9 @@ public class Notification implements Parcelable checkBuilder(); Notification wip = mBuilder.buildUnstyled(); wip.bigContentView = makeBigContentView(); + + wip.extras.putCharSequence(EXTRA_TEXT, mBigText); + return wip; } } @@ -1981,6 +2004,14 @@ public class Notification implements Parcelable checkBuilder(); Notification wip = mBuilder.buildUnstyled(); wip.bigContentView = makeBigContentView(); + + StringBuilder builder = new StringBuilder(); + for (CharSequence str : mTexts) { + builder.append(str); + builder.append("\n"); + } + wip.extras.putCharSequence(EXTRA_TEXT, builder); + return wip; } } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0acad75..5e69128 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -129,8 +129,8 @@ public class NotificationManager } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { - service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut, - UserHandle.myUserId()); + service.enqueueNotificationWithTag(pkg, mContext.getBasePackageName(), tag, id, + notification, idOut, UserHandle.myUserId()); if (id != idOut[0]) { Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); } @@ -151,8 +151,8 @@ public class NotificationManager } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { - service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut, - user.getIdentifier()); + service.enqueueNotificationWithTag(pkg, mContext.getBasePackageName(), tag, id, + notification, idOut, user.getIdentifier()); if (id != idOut[0]) { Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 2897ee0..37804e9 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -41,7 +41,7 @@ import android.util.AndroidException; * you are granting it the right to perform the operation you have specified * as if the other application was yourself (with the same permissions and * identity). As such, you should be careful about how you build the PendingIntent: - * often, for example, the base Intent you supply will have the component + * almost always, for example, the base Intent you supply should have the component * name explicitly set to one of your own components, to ensure it is ultimately * sent there and nowhere else. * @@ -200,6 +200,11 @@ public final class PendingIntent implements Parcelable { * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent. * + * <p class="note">For security reasons, the {@link android.content.Intent} + * you supply here should almost always be an <em>explicit intent</em>, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * * @param context The Context in which this PendingIntent should start * the activity. * @param requestCode Private request code for the sender (currently @@ -227,6 +232,11 @@ public final class PendingIntent implements Parcelable { * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent. * + * <p class="note">For security reasons, the {@link android.content.Intent} + * you supply here should almost always be an <em>explicit intent</em>, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * * @param context The Context in which this PendingIntent should start * the activity. * @param requestCode Private request code for the sender (currently @@ -313,6 +323,11 @@ public final class PendingIntent implements Parcelable { * UI the user actually sees when the intents are started. * </p> * + * <p class="note">For security reasons, the {@link android.content.Intent} objects + * you supply here should almost always be <em>explicit intents</em>, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * * @param context The Context in which this PendingIntent should start * the activity. * @param requestCode Private request code for the sender (currently @@ -359,6 +374,11 @@ public final class PendingIntent implements Parcelable { * UI the user actually sees when the intents are started. * </p> * + * <p class="note">For security reasons, the {@link android.content.Intent} objects + * you supply here should almost always be <em>explicit intents</em>, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * * @param context The Context in which this PendingIntent should start * the activity. * @param requestCode Private request code for the sender (currently @@ -423,6 +443,11 @@ public final class PendingIntent implements Parcelable { * Retrieve a PendingIntent that will perform a broadcast, like calling * {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}. * + * <p class="note">For security reasons, the {@link android.content.Intent} + * you supply here should almost always be an <em>explicit intent</em>, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * * @param context The Context in which this PendingIntent should perform * the broadcast. * @param requestCode Private request code for the sender (currently @@ -473,6 +498,11 @@ public final class PendingIntent implements Parcelable { * {@link Context#startService Context.startService()}. The start * arguments given to the service will come from the extras of the Intent. * + * <p class="note">For security reasons, the {@link android.content.Intent} + * you supply here should almost always be an <em>explicit intent</em>, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * * @param context The Context in which this PendingIntent should start * the service. * @param requestCode Private request code for the sender (currently @@ -707,6 +737,15 @@ public final class PendingIntent implements Parcelable { * sending the Intent. The returned string is supplied by the system, so * that an application can not spoof its package. * + * <p class="note">Be careful about how you use this. All this tells you is + * who created the PendingIntent. It does <strong>not</strong> tell you who + * handed the PendingIntent to you: that is, PendingIntent objects are intended to be + * passed between applications, so the PendingIntent you receive from an application + * could actually be one it received from another application, meaning the result + * you get here will identify the original application. Because of this, you should + * only use this information to identify who you expect to be interacting with + * through a {@link #send} call, not who gave you the PendingIntent.</p> + * * @return The package name of the PendingIntent, or null if there is * none associated with it. */ @@ -726,6 +765,15 @@ public final class PendingIntent implements Parcelable { * sending the Intent. The returned integer is supplied by the system, so * that an application can not spoof its uid. * + * <p class="note">Be careful about how you use this. All this tells you is + * who created the PendingIntent. It does <strong>not</strong> tell you who + * handed the PendingIntent to you: that is, PendingIntent objects are intended to be + * passed between applications, so the PendingIntent you receive from an application + * could actually be one it received from another application, meaning the result + * you get here will identify the original application. Because of this, you should + * only use this information to identify who you expect to be interacting with + * through a {@link #send} call, not who gave you the PendingIntent.</p> + * * @return The uid of the PendingIntent, or -1 if there is * none associated with it. */ @@ -747,6 +795,15 @@ public final class PendingIntent implements Parcelable { * {@link android.os.Process#myUserHandle() Process.myUserHandle()} for * more explanation of user handles. * + * <p class="note">Be careful about how you use this. All this tells you is + * who created the PendingIntent. It does <strong>not</strong> tell you who + * handed the PendingIntent to you: that is, PendingIntent objects are intended to be + * passed between applications, so the PendingIntent you receive from an application + * could actually be one it received from another application, meaning the result + * you get here will identify the original application. Because of this, you should + * only use this information to identify who you expect to be interacting with + * through a {@link #send} call, not who gave you the PendingIntent.</p> + * * @return The user handle of the PendingIntent, or null if there is * none associated with it. */ diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 6382cee..7dfc589 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -846,8 +846,8 @@ public class SearchManager * * @hide */ - public Intent getAssistIntent(Context context) { - return getAssistIntent(context, UserHandle.myUserId()); + public Intent getAssistIntent(Context context, boolean inclContext) { + return getAssistIntent(context, inclContext, UserHandle.myUserId()); } /** @@ -856,7 +856,7 @@ public class SearchManager * * @hide */ - public Intent getAssistIntent(Context context, int userHandle) { + public Intent getAssistIntent(Context context, boolean inclContext, int userHandle) { try { if (mService == null) { return null; @@ -867,6 +867,13 @@ public class SearchManager } Intent intent = new Intent(Intent.ACTION_ASSIST); intent.setComponent(comp); + if (inclContext) { + IActivityManager am = ActivityManagerNative.getDefault(); + Bundle extras = am.getTopActivityExtras(0); + if (extras != null) { + intent.replaceExtras(extras); + } + } return intent; } catch (RemoteException re) { Log.e(TAG, "getAssistIntent() failed: " + re); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java new file mode 100644 index 0000000..7d02342 --- /dev/null +++ b/core/java/android/app/UiAutomation.java @@ -0,0 +1,699 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.accessibilityservice.AccessibilityService.Callbacks; +import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceClient; +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Point; +import android.hardware.display.DisplayManagerGlobal; +import android.os.Looper; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; +import android.view.Display; +import android.view.InputEvent; +import android.view.Surface; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityInteractionClient; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityInteractionConnection; + +import java.util.ArrayList; +import java.util.concurrent.TimeoutException; + +/** + * Class for interacting with the device's UI by simulation user actions and + * introspection of the screen content. It relies on the platform accessibility + * APIs to introspect the screen and to perform some actions on the remote view + * tree. It also allows injecting of arbitrary raw input events simulating user + * interaction with keyboards and touch devices. One can think of a UiAutomation + * as a special type of {@link android.accessibilityservice.AccessibilityService} + * which does not provide hooks for the service life cycle and exposes other + * APIs that are useful for UI test automation. + * <p> + * The APIs exposed by this class are low-level to maximize flexibility when + * developing UI test automation tools and libraries. Generally, a UiAutomation + * client should be using a higher-level library or implement high-level functions. + * For example, performing a tap on the screen requires construction and injecting + * of a touch down and up events which have to be delivered to the system by a + * call to {@link #injectInputEvent(InputEvent, boolean)}. + * </p> + * <p> + * The APIs exposed by this class operate across applications enabling a client + * to write tests that cover use cases spanning over multiple applications. For + * example, going to the settings application to change a setting and then + * interacting with another application whose behavior depends on that setting. + * </p> + */ +public final class UiAutomation { + + private static final String LOG_TAG = UiAutomation.class.getSimpleName(); + + private static final boolean DEBUG = false; + + private static final int CONNECTION_ID_UNDEFINED = -1; + + private static final long CONNECT_TIMEOUT_MILLIS = 5000; + + /** Rotation constant: Unfreeze rotation (rotating the device changes its rotation state). */ + public static final int ROTATION_UNFREEZE = -2; + + /** Rotation constant: Freeze rotation to its current state. */ + public static final int ROTATION_FREEZE_CURRENT = -1; + + /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */ + public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0; + + /** Rotation constant: Freeze rotation to 90 degrees . */ + public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90; + + /** Rotation constant: Freeze rotation to 180 degrees . */ + public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180; + + /** Rotation constant: Freeze rotation to 270 degrees . */ + public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270; + + private final Object mLock = new Object(); + + private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>(); + + private final IAccessibilityServiceClient mClient; + + private final IUiAutomationConnection mUiAutomationConnection; + + private int mConnectionId = CONNECTION_ID_UNDEFINED; + + private OnAccessibilityEventListener mOnAccessibilityEventListener; + + private boolean mWaitingForEventDelivery; + + private long mLastEventTimeMillis; + + private boolean mIsConnecting; + + /** + * Listener for observing the {@link AccessibilityEvent} stream. + */ + public static interface OnAccessibilityEventListener { + + /** + * Callback for receiving an {@link AccessibilityEvent}. + * <p> + * <strong>Note:</strong> This method is <strong>NOT</strong> executed + * on the main test thread. The client is responsible for proper + * synchronization. + * </p> + * <p> + * <strong>Note:</strong> It is responsibility of the client + * to recycle the received events to minimize object creation. + * </p> + * + * @param event The received event. + */ + public void onAccessibilityEvent(AccessibilityEvent event); + } + + /** + * Listener for filtering accessibility events. + */ + public static interface AccessibilityEventFilter { + + /** + * Callback for determining whether an event is accepted or + * it is filtered out. + * + * @param event The event to process. + * @return True if the event is accepted, false to filter it out. + */ + public boolean accept(AccessibilityEvent event); + } + + /** + * Creates a new instance that will handle callbacks from the accessibility + * layer on the thread of the provided looper and perform requests for privileged + * operations on the provided connection. + * + * @param looper The looper on which to execute accessibility callbacks. + * @param connection The connection for performing privileged operations. + * + * @hide + */ + public UiAutomation(Looper looper, IUiAutomationConnection connection) { + if (looper == null) { + throw new IllegalArgumentException("Looper cannot be null!"); + } + if (connection == null) { + throw new IllegalArgumentException("Connection cannot be null!"); + } + mUiAutomationConnection = connection; + mClient = new IAccessibilityServiceClientImpl(looper); + } + + /** + * Connects this UiAutomation to the accessibility introspection APIs. + * + * @hide + */ + public void connect() { + synchronized (mLock) { + throwIfConnectedLocked(); + if (mIsConnecting) { + return; + } + mIsConnecting = true; + } + + try { + // Calling out without a lock held. + mUiAutomationConnection.connect(mClient); + } catch (RemoteException re) { + throw new RuntimeException("Error while connecting UiAutomation", re); + } + + synchronized (mLock) { + final long startTimeMillis = SystemClock.uptimeMillis(); + try { + while (true) { + if (isConnectedLocked()) { + break; + } + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = CONNECT_TIMEOUT_MILLIS - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + throw new RuntimeException("Error while connecting UiAutomation"); + } + try { + mLock.wait(remainingTimeMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } finally { + mIsConnecting = false; + } + } + } + + /** + * Disconnects this UiAutomation from the accessibility introspection APIs. + * + * @hide + */ + public void disconnect() { + synchronized (mLock) { + if (mIsConnecting) { + throw new IllegalStateException( + "Cannot call disconnect() while connecting!"); + } + throwIfNotConnectedLocked(); + mConnectionId = CONNECTION_ID_UNDEFINED; + } + try { + // Calling out without a lock held. + mUiAutomationConnection.disconnect(); + } catch (RemoteException re) { + throw new RuntimeException("Error while disconnecting UiAutomation", re); + } + } + + /** + * The id of the {@link IAccessibilityInteractionConnection} for querying + * the screen content. This is here for legacy purposes since some tools use + * hidden APIs to introspect the screen. + * + * @hide + */ + public int getConnectionId() { + synchronized (mLock) { + throwIfNotConnectedLocked(); + return mConnectionId; + } + } + + /** + * Sets a callback for observing the stream of {@link AccessibilityEvent}s. + * + * @param listener The callback. + */ + public void setOnAccessibilityEventListener(OnAccessibilityEventListener listener) { + synchronized (mLock) { + mOnAccessibilityEventListener = listener; + } + } + + /** + * Performs a global action. Such an action can be performed at any moment + * regardless of the current application or user location in that application. + * For example going back, going home, opening recents, etc. + * + * @param action The action to perform. + * @return Whether the action was successfully performed. + * + * @see AccessibilityService#GLOBAL_ACTION_BACK + * @see AccessibilityService#GLOBAL_ACTION_HOME + * @see AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS + * @see AccessibilityService#GLOBAL_ACTION_RECENTS + */ + public final boolean performGlobalAction(int action) { + final IAccessibilityServiceConnection connection; + synchronized (mLock) { + throwIfNotConnectedLocked(); + connection = AccessibilityInteractionClient.getInstance() + .getConnection(mConnectionId); + } + // Calling out without a lock held. + if (connection != null) { + try { + return connection.performGlobalAction(action); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling performGlobalAction", re); + } + } + return false; + } + + /** + * Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation. + * This method is useful if one wants to change some of the dynamically + * configurable properties at runtime. + * + * @return The accessibility service info. + * + * @see AccessibilityServiceInfo + */ + public final AccessibilityServiceInfo getServiceInfo() { + final IAccessibilityServiceConnection connection; + synchronized (mLock) { + throwIfNotConnectedLocked(); + connection = AccessibilityInteractionClient.getInstance() + .getConnection(mConnectionId); + } + // Calling out without a lock held. + if (connection != null) { + try { + return connection.getServiceInfo(); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); + } + } + return null; + } + + /** + * Sets the {@link AccessibilityServiceInfo} that describes how this + * UiAutomation will be handled by the platform accessibility layer. + * + * @param info The info. + * + * @see AccessibilityServiceInfo + */ + public final void setServiceInfo(AccessibilityServiceInfo info) { + final IAccessibilityServiceConnection connection; + synchronized (mLock) { + throwIfNotConnectedLocked(); + AccessibilityInteractionClient.getInstance().clearCache(); + connection = AccessibilityInteractionClient.getInstance() + .getConnection(mConnectionId); + } + // Calling out without a lock held. + if (connection != null) { + try { + connection.setServiceInfo(info); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); + } + } + } + + /** + * Gets the root {@link AccessibilityNodeInfo} in the active window. + * + * @return The root info. + */ + public AccessibilityNodeInfo getRootInActiveWindow() { + final int connectionId; + synchronized (mLock) { + throwIfNotConnectedLocked(); + connectionId = mConnectionId; + } + // Calling out without a lock held. + return AccessibilityInteractionClient.getInstance() + .getRootInActiveWindow(connectionId); + } + + /** + * A method for injecting an arbitrary input event. + * <p> + * <strong>Note:</strong> It is caller's responsibility to recycle the event. + * </p> + * @param event The event to inject. + * @param sync Whether to inject the event synchronously. + * @return Whether event injection succeeded. + */ + public boolean injectInputEvent(InputEvent event, boolean sync) { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + try { + if (DEBUG) { + Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync); + } + // Calling out without a lock held. + return mUiAutomationConnection.injectInputEvent(event, sync); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while injecting input event!", re); + } + return false; + } + + /** + * Sets the device rotation. A client can freeze the rotation in + * desired state or freeze the rotation to its current state or + * unfreeze the rotation (rotating the device changes its rotation + * state). + * + * @param rotation The desired rotation. + * @return Whether the rotation was set successfully. + * + * @see #ROTATION_FREEZE_0 + * @see #ROTATION_FREEZE_90 + * @see #ROTATION_FREEZE_180 + * @see #ROTATION_FREEZE_270 + * @see #ROTATION_FREEZE_CURRENT + * @see #ROTATION_UNFREEZE + */ + public boolean setRotation(int rotation) { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + switch (rotation) { + case ROTATION_FREEZE_0: + case ROTATION_FREEZE_90: + case ROTATION_FREEZE_180: + case ROTATION_FREEZE_270: + case ROTATION_UNFREEZE: + case ROTATION_FREEZE_CURRENT: { + try { + // Calling out without a lock held. + mUiAutomationConnection.setRotation(rotation); + return true; + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while setting rotation!", re); + } + } return false; + default: { + throw new IllegalArgumentException("Invalid rotation."); + } + } + } + + /** + * Executes a command and waits for a specific accessibility event up to a + * given wait timeout. To detect a sequence of events one can implement a + * filter that keeps track of seen events of the expected sequence and + * returns true after the last event of that sequence is received. + * <p> + * <strong>Note:</strong> It is caller's responsibility to recycle the returned event. + * </p> + * @param command The command to execute. + * @param filter Filter that recognizes the expected event. + * @param timeoutMillis The wait timeout in milliseconds. + * + * @throws TimeoutException If the expected event is not received within the timeout. + */ + public AccessibilityEvent executeAndWaitForEvent(Runnable command, + AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException { + synchronized (mLock) { + throwIfNotConnectedLocked(); + + mEventQueue.clear(); + // Prepare to wait for an event. + mWaitingForEventDelivery = true; + + // We will ignore events from previous interactions. + final long executionStartTimeMillis = SystemClock.uptimeMillis(); + + // Execute the command. + command.run(); + try { + // Wait for the event. + final long startTimeMillis = SystemClock.uptimeMillis(); + while (true) { + // Drain the event queue + while (!mEventQueue.isEmpty()) { + AccessibilityEvent event = mEventQueue.remove(0); + // Ignore events from previous interactions. + if (event.getEventTime() <= executionStartTimeMillis) { + continue; + } + if (filter.accept(event)) { + return event; + } + event.recycle(); + } + // Check if timed out and if not wait. + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + throw new TimeoutException("Expected event not received within: " + + timeoutMillis + " ms."); + } + try { + mLock.wait(remainingTimeMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } finally { + mWaitingForEventDelivery = false; + mEventQueue.clear(); + mLock.notifyAll(); + } + } + } + + /** + * Waits for the accessibility event stream to become idle, which is not to + * have received an accessibility event within <code>idleTimeoutMillis</code>. + * The total time spent to wait for an idle accessibility event stream is bounded + * by the <code>globalTimeoutMillis</code>. + * + * @param idleTimeoutMillis The timeout in milliseconds between two events + * to consider the device idle. + * @param globalTimeoutMillis The maximal global timeout in milliseconds in + * which to wait for an idle state. + * + * @throws TimeoutException If no idle state was detected within + * <code>globalTimeoutMillis.</code> + */ + public void waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis) + throws TimeoutException { + synchronized (mLock) { + throwIfNotConnectedLocked(); + + final long startTimeMillis = SystemClock.uptimeMillis(); + if (mLastEventTimeMillis <= 0) { + mLastEventTimeMillis = startTimeMillis; + } + + while (true) { + final long currentTimeMillis = SystemClock.uptimeMillis(); + // Did we get idle state within the global timeout? + final long elapsedGlobalTimeMillis = currentTimeMillis - startTimeMillis; + final long remainingGlobalTimeMillis = + globalTimeoutMillis - elapsedGlobalTimeMillis; + if (remainingGlobalTimeMillis <= 0) { + throw new TimeoutException("No idle state with idle timeout: " + + idleTimeoutMillis + " within global timeout: " + + globalTimeoutMillis); + } + // Did we get an idle state within the idle timeout? + final long elapsedIdleTimeMillis = currentTimeMillis - mLastEventTimeMillis; + final long remainingIdleTimeMillis = idleTimeoutMillis - elapsedIdleTimeMillis; + if (remainingIdleTimeMillis <= 0) { + return; + } + try { + mLock.wait(remainingIdleTimeMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } + } + + /** + * Takes a screenshot. + * + * @return The screenshot bitmap on success, null otherwise. + */ + public Bitmap takeScreenshot() { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + Display display = DisplayManagerGlobal.getInstance() + .getRealDisplay(Display.DEFAULT_DISPLAY); + Point displaySize = new Point(); + display.getRealSize(displaySize); + final int displayWidth = displaySize.x; + final int displayHeight = displaySize.y; + + final float screenshotWidth; + final float screenshotHeight; + + final int rotation = display.getRotation(); + switch (rotation) { + case ROTATION_FREEZE_0: { + screenshotWidth = displayWidth; + screenshotHeight = displayHeight; + } break; + case ROTATION_FREEZE_90: { + screenshotWidth = displayHeight; + screenshotHeight = displayWidth; + } break; + case ROTATION_FREEZE_180: { + screenshotWidth = displayWidth; + screenshotHeight = displayHeight; + } break; + case ROTATION_FREEZE_270: { + screenshotWidth = displayHeight; + screenshotHeight = displayWidth; + } break; + default: { + throw new IllegalArgumentException("Invalid rotation: " + + rotation); + } + } + + // Take the screenshot + Bitmap screenShot = null; + try { + // Calling out without a lock held. + screenShot = mUiAutomationConnection.takeScreenshot((int) screenshotWidth, + (int) screenshotHeight); + if (screenShot == null) { + return null; + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while taking screnshot!", re); + return null; + } + + // Rotate the screenshot to the current orientation + if (rotation != ROTATION_FREEZE_0) { + Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(unrotatedScreenShot); + canvas.translate(unrotatedScreenShot.getWidth() / 2, + unrotatedScreenShot.getHeight() / 2); + canvas.rotate(getDegreesForRotation(rotation)); + canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2); + canvas.drawBitmap(screenShot, 0, 0, null); + canvas.setBitmap(null); + screenShot = unrotatedScreenShot; + } + + // Optimization + screenShot.setHasAlpha(false); + + return screenShot; + } + + private static float getDegreesForRotation(int value) { + switch (value) { + case Surface.ROTATION_90: { + return 360f - 90f; + } + case Surface.ROTATION_180: { + return 360f - 180f; + } + case Surface.ROTATION_270: { + return 360f - 270f; + } default: { + return 0; + } + } + } + + private boolean isConnectedLocked() { + return mConnectionId != CONNECTION_ID_UNDEFINED; + } + + private void throwIfConnectedLocked() { + if (mConnectionId != CONNECTION_ID_UNDEFINED) { + throw new IllegalStateException("UiAutomation not connected!"); + } + } + + private void throwIfNotConnectedLocked() { + if (!isConnectedLocked()) { + throw new IllegalStateException("UiAutomation not connected!"); + } + } + + private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper { + + public IAccessibilityServiceClientImpl(Looper looper) { + super(null, looper, new Callbacks() { + @Override + public void onSetConnectionId(int connectionId) { + synchronized (mLock) { + mConnectionId = connectionId; + mLock.notifyAll(); + } + } + + @Override + public void onServiceConnected() { + /* do nothing */ + } + + @Override + public void onInterrupt() { + /* do nothing */ + } + + @Override + public boolean onGesture(int gestureId) { + /* do nothing */ + return false; + } + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + synchronized (mLock) { + mLastEventTimeMillis = event.getEventTime(); + if (mWaitingForEventDelivery) { + mEventQueue.add(AccessibilityEvent.obtain(event)); + } + mLock.notifyAll(); + } + // Calling out only without a lock held. + final OnAccessibilityEventListener listener = mOnAccessibilityEventListener; + if (listener != null) { + listener.onAccessibilityEvent(AccessibilityEvent.obtain(event)); + } + } + }); + } + } +} diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java new file mode 100644 index 0000000..06ef472 --- /dev/null +++ b/core/java/android/app/UiAutomationConnection.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceClient; +import android.content.Context; +import android.graphics.Bitmap; +import android.hardware.input.InputManager; +import android.os.Binder; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.view.IWindowManager; +import android.view.InputEvent; +import android.view.SurfaceControl; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.IAccessibilityManager; + +/** + * This is a remote object that is passed from the shell to an instrumentation + * for enabling access to privileged operations which the shell can do and the + * instrumentation cannot. These privileged operations are needed for implementing + * a {@link UiAutomation} that enables across application testing by simulating + * user actions and performing screen introspection. + * + * @hide + */ +public final class UiAutomationConnection extends IUiAutomationConnection.Stub { + + private static final int INITIAL_FROZEN_ROTATION_UNSPECIFIED = -1; + + private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Service.WINDOW_SERVICE)); + + private final Object mLock = new Object(); + + private int mInitialFrozenRotation = INITIAL_FROZEN_ROTATION_UNSPECIFIED; + + private IAccessibilityServiceClient mClient; + + private boolean mIsShutdown; + + private int mOwningUid; + + public void connect(IAccessibilityServiceClient client) { + if (client == null) { + throw new IllegalArgumentException("Client cannot be null!"); + } + synchronized (mLock) { + throwIfShutdownLocked(); + if (isConnectedLocked()) { + throw new IllegalStateException("Already connected."); + } + mOwningUid = Binder.getCallingUid(); + registerUiTestAutomationServiceLocked(client); + storeRotationStateLocked(); + } + } + + @Override + public void disconnect() { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + if (!isConnectedLocked()) { + throw new IllegalStateException("Already disconnected."); + } + mOwningUid = -1; + unregisterUiTestAutomationServiceLocked(); + restoreRotationStateLocked(); + } + } + + @Override + public boolean injectInputEvent(InputEvent event, boolean sync) { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + final int mode = (sync) ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH + : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; + final long identity = Binder.clearCallingIdentity(); + try { + return InputManager.getInstance().injectInputEvent(event, mode); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public boolean setRotation(int rotation) { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + final long identity = Binder.clearCallingIdentity(); + try { + if (rotation == UiAutomation.ROTATION_UNFREEZE) { + mWindowManager.thawRotation(); + } else { + mWindowManager.freezeRotation(rotation); + } + return true; + } catch (RemoteException re) { + /* ignore */ + } finally { + Binder.restoreCallingIdentity(identity); + } + return false; + } + + @Override + public Bitmap takeScreenshot(int width, int height) { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + final long identity = Binder.clearCallingIdentity(); + try { + return SurfaceControl.screenshot(width, height); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void shutdown() { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + mIsShutdown = true; + if (isConnectedLocked()) { + disconnect(); + } + } + } + + private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client) { + IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; + info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; + info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS + | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS; + try { + // Calling out with a lock held is fine since if the system + // process is gone the client calling in will be killed. + manager.registerUiTestAutomationService(client, info); + mClient = client; + } catch (RemoteException re) { + throw new IllegalStateException("Error while registering UiTestAutomationService.", re); + } + } + + private void unregisterUiTestAutomationServiceLocked() { + IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); + try { + // Calling out with a lock held is fine since if the system + // process is gone the client calling in will be killed. + manager.unregisterUiTestAutomationService(mClient); + mClient = null; + } catch (RemoteException re) { + throw new IllegalStateException("Error while unregistering UiTestAutomationService", + re); + } + } + + private void storeRotationStateLocked() { + try { + if (mWindowManager.isRotationFrozen()) { + // Calling out with a lock held is fine since if the system + // process is gone the client calling in will be killed. + mInitialFrozenRotation = mWindowManager.getRotation(); + } + } catch (RemoteException re) { + /* ignore */ + } + } + + private void restoreRotationStateLocked() { + try { + if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) { + // Calling out with a lock held is fine since if the system + // process is gone the client calling in will be killed. + mWindowManager.freezeRotation(mInitialFrozenRotation); + } else { + // Calling out with a lock held is fine since if the system + // process is gone the client calling in will be killed. + mWindowManager.thawRotation(); + } + } catch (RemoteException re) { + /* ignore */ + } + } + + private boolean isConnectedLocked() { + return mClient != null; + } + + private void throwIfShutdownLocked() { + if (mIsShutdown) { + throw new IllegalStateException("Connection shutdown!"); + } + } + + private void throwIfNotConnectedLocked() { + if (!isConnectedLocked()) { + throw new IllegalStateException("Not connected!"); + } + } + + private void throwIfCalledByNotTrustedUidLocked() { + final int callingUid = Binder.getCallingUid(); + if (callingUid != mOwningUid && mOwningUid != Process.SYSTEM_UID + && callingUid != 0 /*root*/) { + throw new SecurityException("Calling from not trusted UID!"); + } + } +} diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 9ad33a5..b678df7 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -227,6 +227,7 @@ public abstract class BackupAgent extends ContextWrapper { String libDir = (appInfo.nativeLibraryDir != null) ? new File(appInfo.nativeLibraryDir).getCanonicalPath() : null; + String externalFilesDir = getExternalFilesDir(null).getCanonicalPath(); // Filters, the scan queue, and the set of resulting entities HashSet<String> filterSet = new HashSet<String>(); @@ -254,6 +255,12 @@ public abstract class BackupAgent extends ContextWrapper { filterSet.add(databaseDir); filterSet.remove(sharedPrefsDir); fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data); + + // getExternalFilesDir() location associated with this app. Technically there should + // not be any files here if the app does not properly have permission to access + // external storage, but edge cases happen. fullBackupFileTree() catches + // IOExceptions and similar, and treats them as non-fatal, so we rely on that here. + fullBackupFileTree(packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, externalFilesDir, null, data); } /** @@ -274,6 +281,7 @@ public abstract class BackupAgent extends ContextWrapper { String spDir; String cacheDir; String libDir; + String efDir; String filePath; ApplicationInfo appInfo = getApplicationInfo(); @@ -287,6 +295,7 @@ public abstract class BackupAgent extends ContextWrapper { libDir = (appInfo.nativeLibraryDir == null) ? null : new File(appInfo.nativeLibraryDir).getCanonicalPath(); + efDir = getExternalFilesDir(null).getCanonicalPath(); // Now figure out which well-defined tree the file is placed in, working from // most to least specific. We also specifically exclude the lib and cache dirs. @@ -315,6 +324,9 @@ public abstract class BackupAgent extends ContextWrapper { } else if (filePath.startsWith(mainDir)) { domain = FullBackup.ROOT_TREE_TOKEN; rootpath = mainDir; + } else if (filePath.startsWith(efDir)) { + domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN; + rootpath = efDir; } else { Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping"); return; @@ -438,6 +450,8 @@ public abstract class BackupAgent extends ContextWrapper { basePath = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) { basePath = getCacheDir().getCanonicalPath(); + } else if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) { + basePath = getExternalFilesDir(null).getCanonicalPath(); } else { // Not a supported location Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring"); diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index f859599..2fe08f3 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -46,6 +46,7 @@ public class FullBackup { public static final String DATA_TREE_TOKEN = "f"; public static final String DATABASE_TREE_TOKEN = "db"; public static final String SHAREDPREFS_TREE_TOKEN = "sp"; + public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef"; public static final String CACHE_TREE_TOKEN = "c"; public static final String SHARED_STORAGE_TOKEN = "shared"; diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index fa3bf4d..a470e70 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.util.DisplayMetrics; +import android.util.Log; import android.util.TypedValue; import android.widget.RemoteViews; import android.widget.RemoteViews.OnClickHandler; @@ -55,38 +56,39 @@ public class AppWidgetHost { Context mContext; String mPackageName; + Handler mHandler; + int mHostId; + Callbacks mCallbacks = new Callbacks(); + final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>(); + private OnClickHandler mOnClickHandler; class Callbacks extends IAppWidgetHost.Stub { - public void updateAppWidget(int appWidgetId, RemoteViews views) { + public void updateAppWidget(int appWidgetId, RemoteViews views, int userId) { if (isLocalBinder() && views != null) { views = views.clone(); - views.setUser(mUser); + views.setUser(new UserHandle(userId)); } - Message msg = mHandler.obtainMessage(HANDLE_UPDATE); - msg.arg1 = appWidgetId; - msg.obj = views; + Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, userId, views); msg.sendToTarget(); } - public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) { + public void providerChanged(int appWidgetId, AppWidgetProviderInfo info, int userId) { if (isLocalBinder() && info != null) { info = info.clone(); } - Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED); - msg.arg1 = appWidgetId; - msg.obj = info; + Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED, + appWidgetId, userId, info); msg.sendToTarget(); } - public void providersChanged() { - Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED); + public void providersChanged(int userId) { + Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED, userId, 0); msg.sendToTarget(); } - public void viewDataChanged(int appWidgetId, int viewId) { - Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED); - msg.arg1 = appWidgetId; - msg.arg2 = viewId; + public void viewDataChanged(int appWidgetId, int viewId, int userId) { + Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED, + appWidgetId, viewId, userId); msg.sendToTarget(); } } @@ -99,7 +101,7 @@ public class AppWidgetHost { public void handleMessage(Message msg) { switch (msg.what) { case HANDLE_UPDATE: { - updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj); + updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj, msg.arg2); break; } case HANDLE_PROVIDER_CHANGED: { @@ -107,26 +109,17 @@ public class AppWidgetHost { break; } case HANDLE_PROVIDERS_CHANGED: { - onProvidersChanged(); + onProvidersChanged(msg.arg1); break; } case HANDLE_VIEW_DATA_CHANGED: { - viewDataChanged(msg.arg1, msg.arg2); + viewDataChanged(msg.arg1, msg.arg2, (Integer) msg.obj); break; } } } } - Handler mHandler; - - int mHostId; - Callbacks mCallbacks = new Callbacks(); - final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>(); - private OnClickHandler mOnClickHandler; - // Optionally set by lockscreen - private UserHandle mUser; - public AppWidgetHost(Context context, int hostId) { this(context, hostId, null, context.getMainLooper()); } @@ -140,14 +133,9 @@ public class AppWidgetHost { mOnClickHandler = handler; mHandler = new UpdateHandler(looper); mDisplayMetrics = context.getResources().getDisplayMetrics(); - mUser = Process.myUserHandle(); bindService(); } - /** @hide */ - public void setUserId(int userId) { - mUser = new UserHandle(userId); - } private static void bindService() { synchronized (sServiceLock) { @@ -163,23 +151,15 @@ public class AppWidgetHost { * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { - startListeningAsUser(UserHandle.myUserId()); - } - - /** - * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity - * becomes visible, i.e. from onStart() in your Activity. - * @hide - */ - public void startListeningAsUser(int userId) { int[] updatedIds; ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>(); + final int userId = mContext.getUserId(); try { if (mPackageName == null) { mPackageName = mContext.getPackageName(); } - updatedIds = sService.startListeningAsUser( + updatedIds = sService.startListening( mCallbacks, mPackageName, mHostId, updatedViews, userId); } catch (RemoteException e) { @@ -191,7 +171,7 @@ public class AppWidgetHost { if (updatedViews.get(i) != null) { updatedViews.get(i).setUser(new UserHandle(userId)); } - updateAppWidgetView(updatedIds[i], updatedViews.get(i)); + updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId); } } @@ -201,26 +181,14 @@ public class AppWidgetHost { */ public void stopListening() { try { - sService.stopListeningAsUser(mHostId, UserHandle.myUserId()); + sService.stopListening(mHostId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } - } - /** - * Stop receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity is - * no longer visible, i.e. from onStop() in your Activity. - * @hide - */ - public void stopListeningAsUser(int userId) { - try { - sService.stopListeningAsUser(mHostId, userId); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - // Also clear the views + // This is here because keyguard needs it since it'll be switching users after this call. + // If it turns out other apps need to call this often, we should re-think how this works. clearViews(); } @@ -230,11 +198,12 @@ public class AppWidgetHost { * @return a appWidgetId */ public int allocateAppWidgetId() { + try { if (mPackageName == null) { mPackageName = mContext.getPackageName(); } - return sService.allocateAppWidgetId(mPackageName, mHostId); + return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -247,7 +216,7 @@ public class AppWidgetHost { * @return a appWidgetId * @hide */ - public static int allocateAppWidgetIdForSystem(int hostId) { + public static int allocateAppWidgetIdForSystem(int hostId, int userId) { checkCallerIsSystem(); try { if (sService == null) { @@ -256,7 +225,7 @@ public class AppWidgetHost { Context systemContext = (Context) ActivityThread.currentActivityThread().getSystemContext(); String packageName = systemContext.getPackageName(); - return sService.allocateAppWidgetId(packageName, hostId); + return sService.allocateAppWidgetId(packageName, hostId, userId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } @@ -272,7 +241,7 @@ public class AppWidgetHost { if (sService == null) { bindService(); } - return sService.getAppWidgetIdsForHost(mHostId); + return sService.getAppWidgetIdsForHost(mHostId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } @@ -297,7 +266,7 @@ public class AppWidgetHost { synchronized (mViews) { mViews.remove(appWidgetId); try { - sService.deleteAppWidgetId(appWidgetId); + sService.deleteAppWidgetId(appWidgetId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -309,13 +278,13 @@ public class AppWidgetHost { * Stop listening to changes for this AppWidget. * @hide */ - public static void deleteAppWidgetIdForSystem(int appWidgetId) { + public static void deleteAppWidgetIdForSystem(int appWidgetId, int userId) { checkCallerIsSystem(); try { if (sService == null) { bindService(); } - sService.deleteAppWidgetId(appWidgetId); + sService.deleteAppWidgetId(appWidgetId, userId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } @@ -331,7 +300,7 @@ public class AppWidgetHost { */ public void deleteHost() { try { - sService.deleteHost(mHostId); + sService.deleteHost(mHostId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -347,8 +316,16 @@ public class AppWidgetHost { * </ul> */ public static void deleteAllHosts() { + deleteAllHosts(UserHandle.myUserId()); + } + + /** + * Private method containing a userId + * @hide + */ + public static void deleteAllHosts(int userId) { try { - sService.deleteAllHosts(); + sService.deleteAllHosts(userId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -361,8 +338,9 @@ public class AppWidgetHost { */ public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { + final int userId = context.getUserId(); AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); - view.setUserId(mUser.getIdentifier()); + view.setUserId(userId); view.setOnClickHandler(mOnClickHandler); view.setAppWidget(appWidgetId, appWidget); synchronized (mViews) { @@ -370,9 +348,9 @@ public class AppWidgetHost { } RemoteViews views; try { - views = sService.getAppWidgetViews(appWidgetId); + views = sService.getAppWidgetViews(appWidgetId, userId); if (views != null) { - views.setUser(mUser); + views.setUser(new UserHandle(mContext.getUserId())); } } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -422,10 +400,20 @@ public class AppWidgetHost { * are added, updated or removed, or widget components are enabled or disabled.) */ protected void onProvidersChanged() { - // Do nothing + onProvidersChanged(mContext.getUserId()); } - void updateAppWidgetView(int appWidgetId, RemoteViews views) { + /** + * Private method containing a userId + * @hide + */ + protected void onProvidersChanged(int userId) { + checkUserMatch(userId); + // Does nothing + } + + void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) { + checkUserMatch(userId); AppWidgetHostView v; synchronized (mViews) { v = mViews.get(appWidgetId); @@ -435,7 +423,8 @@ public class AppWidgetHost { } } - void viewDataChanged(int appWidgetId, int viewId) { + void viewDataChanged(int appWidgetId, int viewId, int userId) { + checkUserMatch(userId); AppWidgetHostView v; synchronized (mViews) { v = mViews.get(appWidgetId); @@ -445,6 +434,16 @@ public class AppWidgetHost { } } + // Ensure that the userId passed to us agrees with the one associated with this instance + // of AppWidgetHost. + // TODO: This should be removed in production code. + private void checkUserMatch(int userId) { + if (userId != mContext.getUserId()) { + throw new IllegalStateException( + "User ids don't match, userId=" + userId + ", mUserId=" + mContext.getUserId()); + } + } + /** * Clear the list of Views that have been created by this AppWidgetHost. */ diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 6b1c3e2..e68d23a 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -16,6 +16,7 @@ package android.appwidget; +import android.app.ActivityManagerNative; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -268,8 +269,8 @@ public class AppWidgetManager { /** * Sent when the custom extras for an AppWidget change. * - * @see AppWidgetProvider#onAppWidgetOptionsChanged - * AppWidgetProvider.onAppWidgetOptionsChanged(Context context, + * @see AppWidgetProvider#onAppWidgetOptionsChanged + * AppWidgetProvider.onAppWidgetOptionsChanged(Context context, * AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras) */ public static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS"; @@ -352,7 +353,7 @@ public class AppWidgetManager { * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, * and outside of the handler. * This method will only work when called from the uid that owns the AppWidget provider. - * + * * <p> * The total Bitmap memory used by the RemoteViews object cannot exceed that required to * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes. @@ -362,7 +363,7 @@ public class AppWidgetManager { */ public void updateAppWidget(int[] appWidgetIds, RemoteViews views) { try { - sService.updateAppWidgetIds(appWidgetIds, views); + sService.updateAppWidgetIds(appWidgetIds, views, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -382,7 +383,7 @@ public class AppWidgetManager { */ public void updateAppWidgetOptions(int appWidgetId, Bundle options) { try { - sService.updateAppWidgetOptions(appWidgetId, options); + sService.updateAppWidgetOptions(appWidgetId, options, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -402,7 +403,7 @@ public class AppWidgetManager { */ public Bundle getAppWidgetOptions(int appWidgetId) { try { - return sService.getAppWidgetOptions(appWidgetId); + return sService.getAppWidgetOptions(appWidgetId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -436,7 +437,7 @@ public class AppWidgetManager { * Perform an incremental update or command on the widget(s) specified by appWidgetIds. * * This update differs from {@link #updateAppWidget(int[], RemoteViews)} in that the - * RemoteViews object which is passed is understood to be an incomplete representation of the + * RemoteViews object which is passed is understood to be an incomplete representation of the * widget, and hence does not replace the cached representation of the widget. As of API * level 17, the new properties set within the views objects will be appended to the cached * representation of the widget, and hence will persist. @@ -458,7 +459,7 @@ public class AppWidgetManager { */ public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) { try { - sService.partiallyUpdateAppWidgetIds(appWidgetIds, views); + sService.partiallyUpdateAppWidgetIds(appWidgetIds, views, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } @@ -507,7 +508,7 @@ public class AppWidgetManager { */ public void updateAppWidget(ComponentName provider, RemoteViews views) { try { - sService.updateAppWidgetProvider(provider, views); + sService.updateAppWidgetProvider(provider, views, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -523,7 +524,7 @@ public class AppWidgetManager { */ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) { try { - sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId); + sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -557,7 +558,8 @@ public class AppWidgetManager { */ public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) { try { - List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter); + List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter, + mContext.getUserId()); for (AppWidgetProviderInfo info : providers) { // Converting complex to dp. info.minWidth = @@ -584,7 +586,8 @@ public class AppWidgetManager { */ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { try { - AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId); + AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId, + mContext.getUserId()); if (info != null) { // Converting complex to dp. info.minWidth = @@ -617,7 +620,7 @@ public class AppWidgetManager { */ public void bindAppWidgetId(int appWidgetId, ComponentName provider) { try { - sService.bindAppWidgetId(appWidgetId, provider, null); + sService.bindAppWidgetId(appWidgetId, provider, null, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -641,7 +644,7 @@ public class AppWidgetManager { */ public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) { try { - sService.bindAppWidgetId(appWidgetId, provider, options); + sService.bindAppWidgetId(appWidgetId, provider, options, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -667,7 +670,7 @@ public class AppWidgetManager { } try { return sService.bindAppWidgetIdIfAllowed( - mContext.getPackageName(), appWidgetId, provider, null); + mContext.getPackageName(), appWidgetId, provider, null, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -696,8 +699,8 @@ public class AppWidgetManager { return false; } try { - return sService.bindAppWidgetIdIfAllowed( - mContext.getPackageName(), appWidgetId, provider, options); + return sService.bindAppWidgetIdIfAllowed(mContext.getPackageName(), appWidgetId, + provider, options, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -715,7 +718,7 @@ public class AppWidgetManager { */ public boolean hasBindAppWidgetPermission(String packageName) { try { - return sService.hasBindAppWidgetPermission(packageName); + return sService.hasBindAppWidgetPermission(packageName, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -733,7 +736,7 @@ public class AppWidgetManager { */ public void setBindAppWidgetPermission(String packageName, boolean permission) { try { - sService.setBindAppWidgetPermission(packageName, permission); + sService.setBindAppWidgetPermission(packageName, permission, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -794,7 +797,7 @@ public class AppWidgetManager { */ public int[] getAppWidgetIds(ComponentName provider) { try { - return sService.getAppWidgetIds(provider); + return sService.getAppWidgetIds(provider, mContext.getUserId()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 063e5a8..3ba4f26 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -21,7 +21,6 @@ import android.os.ServiceManager; import android.os.INetworkManagementService; import android.content.Context; import android.net.ConnectivityManager; -import android.net.DhcpInfoInternal; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index f9025d9..188c786 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -78,7 +78,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { // So we treat this case as an unhandled exception. throw ex; } - if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)"); + if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)", ex); return null; } } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 23d8f46..e9b800d 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -18,6 +18,7 @@ package android.content; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import android.app.AppOpsManager; import android.content.pm.PackageManager; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; @@ -154,8 +155,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param abstractInterface The ContentProvider interface that is to be * coerced. - * @return If the IContentProvider is non-null and local, returns its actual - * ContentProvider instance. Otherwise returns null. + * @return If the IContentProvider is non-{@code null} and local, returns its actual + * ContentProvider instance. Otherwise returns {@code null}. * @hide */ public static ContentProvider coerceToLocalContentProvider( @@ -172,6 +173,10 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * @hide */ class Transport extends ContentProviderNative { + AppOpsManager mAppOpsManager = null; + int mReadOp = AppOpsManager.OP_NONE; + int mWriteOp = AppOpsManager.OP_NONE; + ContentProvider getContentProvider() { return ContentProvider.this; } @@ -182,10 +187,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } @Override - public Cursor query(Uri uri, String[] projection, + public Cursor query(String callingPkg, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) { - enforceReadPermission(uri); + if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + return rejectQuery(uri, projection, selection, selectionArgs, sortOrder, + CancellationSignal.fromTransport(cancellationSignal)); + } return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder, CancellationSignal.fromTransport(cancellationSignal)); } @@ -196,64 +204,77 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } @Override - public Uri insert(Uri uri, ContentValues initialValues) { - enforceWritePermission(uri); + public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) { + if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + return rejectInsert(uri, initialValues); + } return ContentProvider.this.insert(uri, initialValues); } @Override - public int bulkInsert(Uri uri, ContentValues[] initialValues) { - enforceWritePermission(uri); + public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) { + if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + return 0; + } return ContentProvider.this.bulkInsert(uri, initialValues); } @Override - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) + public ContentProviderResult[] applyBatch(String callingPkg, + ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { for (ContentProviderOperation operation : operations) { if (operation.isReadOperation()) { - enforceReadPermission(operation.getUri()); + if (enforceReadPermission(callingPkg, operation.getUri()) + != AppOpsManager.MODE_ALLOWED) { + throw new OperationApplicationException("App op not allowed", 0); + } } if (operation.isWriteOperation()) { - enforceWritePermission(operation.getUri()); + if (enforceWritePermission(callingPkg, operation.getUri()) + != AppOpsManager.MODE_ALLOWED) { + throw new OperationApplicationException("App op not allowed", 0); + } } } return ContentProvider.this.applyBatch(operations); } @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - enforceWritePermission(uri); + public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) { + if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + return 0; + } return ContentProvider.this.delete(uri, selection, selectionArgs); } @Override - public int update(Uri uri, ContentValues values, String selection, + public int update(String callingPkg, Uri uri, ContentValues values, String selection, String[] selectionArgs) { - enforceWritePermission(uri); + if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + return 0; + } return ContentProvider.this.update(uri, values, selection, selectionArgs); } @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) + public ParcelFileDescriptor openFile(String callingPkg, Uri uri, String mode) throws FileNotFoundException { - if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); - else enforceReadPermission(uri); + enforceFilePermission(callingPkg, uri, mode); return ContentProvider.this.openFile(uri, mode); } @Override - public AssetFileDescriptor openAssetFile(Uri uri, String mode) + public AssetFileDescriptor openAssetFile(String callingPkg, Uri uri, String mode) throws FileNotFoundException { - if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); - else enforceReadPermission(uri); + enforceFilePermission(callingPkg, uri, mode); return ContentProvider.this.openAssetFile(uri, mode); } @Override - public Bundle call(String method, String arg, Bundle extras) { - return ContentProvider.this.call(method, arg, extras); + public Bundle call(String callingPkg, String method, String arg, Bundle extras) { + return ContentProvider.this.callFromPackage(callingPkg, method, arg, extras); } @Override @@ -262,9 +283,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } @Override - public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeType, Bundle opts) - throws FileNotFoundException { - enforceReadPermission(uri); + public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType, + Bundle opts) throws FileNotFoundException { + enforceFilePermission(callingPkg, uri, "r"); return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts); } @@ -273,7 +294,28 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return CancellationSignal.createTransport(); } - private void enforceReadPermission(Uri uri) throws SecurityException { + private void enforceFilePermission(String callingPkg, Uri uri, String mode) + throws FileNotFoundException, SecurityException { + if (mode != null && mode.startsWith("rw")) { + if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + throw new FileNotFoundException("App op not allowed"); + } + } else { + if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { + throw new FileNotFoundException("App op not allowed"); + } + } + } + + private int enforceReadPermission(String callingPkg, Uri uri) throws SecurityException { + enforceReadPermissionInner(uri); + if (mReadOp != AppOpsManager.OP_NONE) { + return mAppOpsManager.noteOp(mReadOp, Binder.getCallingUid(), callingPkg); + } + return AppOpsManager.MODE_ALLOWED; + } + + private void enforceReadPermissionInner(Uri uri) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -334,7 +376,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { + ", uid=" + uid + failReason); } - private void enforceWritePermission(Uri uri) throws SecurityException { + private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException { + enforceWritePermissionInner(uri); + if (mWriteOp != AppOpsManager.OP_NONE) { + return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg); + } + return AppOpsManager.MODE_ALLOWED; + } + + private void enforceWritePermissionInner(Uri uri) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -398,7 +448,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { /** * Retrieves the Context this provider is running in. Only available once - * {@link #onCreate} has been called -- this will return null in the + * {@link #onCreate} has been called -- this will return {@code null} in the * constructor. */ public final Context getContext() { @@ -471,6 +521,19 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return mPathPermissions; } + /** @hide */ + public final void setAppOps(int readOp, int writeOp) { + mTransport.mAppOpsManager = (AppOpsManager)mContext.getSystemService( + Context.APP_OPS_SERVICE); + mTransport.mReadOp = readOp; + mTransport.mWriteOp = writeOp; + } + + /** @hide */ + public AppOpsManager getAppOpsManager() { + return mTransport.mAppOpsManager; + } + /** * Implement this to initialize your content provider on startup. * This method is called for all registered content providers on the @@ -526,6 +589,31 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } /** + * @hide + * Implementation when a caller has performed a query on the content + * provider, but that call has been rejected for the operation given + * to {@link #setAppOps(int, int)}. The default implementation + * rewrites the <var>selection</var> argument to include a condition + * that is never true (so will always result in an empty cursor) + * and calls through to {@link #query(android.net.Uri, String[], String, String[], + * String, android.os.CancellationSignal)} with that. + */ + public Cursor rejectQuery(Uri uri, String[] projection, + String selection, String[] selectionArgs, String sortOrder, + CancellationSignal cancellationSignal) { + // The read is not allowed... to fake it out, we replace the given + // selection statement with a dummy one that will always be false. + // This way we will get a cursor back that has the correct structure + // but contains no rows. + if (selection == null) { + selection = "'A' = 'B'"; + } else { + selection = "'A' = 'B' AND (" + selection + ")"; + } + return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); + } + + /** * Implement this to handle query requests from clients. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes @@ -570,15 +658,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * that the implementation should parse and add to a WHERE or HAVING clause, specifying * that _id value. * @param projection The list of columns to put into the cursor. If - * null all columns are included. + * {@code null} all columns are included. * @param selection A selection criteria to apply when filtering rows. - * If null then all rows are included. + * If {@code null} then all rows are included. * @param selectionArgs You may include ?s in selection, which will be replaced by * the values from selectionArgs, in order that they appear in the selection. * The values will be bound as Strings. * @param sortOrder How the rows in the cursor should be sorted. - * If null then the provider is free to define the sort order. - * @return a Cursor or null. + * If {@code null} then the provider is free to define the sort order. + * @return a Cursor or {@code null}. */ public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); @@ -633,18 +721,18 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * that the implementation should parse and add to a WHERE or HAVING clause, specifying * that _id value. * @param projection The list of columns to put into the cursor. If - * null all columns are included. + * {@code null} all columns are included. * @param selection A selection criteria to apply when filtering rows. - * If null then all rows are included. + * If {@code null} then all rows are included. * @param selectionArgs You may include ?s in selection, which will be replaced by * the values from selectionArgs, in order that they appear in the selection. * The values will be bound as Strings. * @param sortOrder How the rows in the cursor should be sorted. - * If null then the provider is free to define the sort order. - * @param cancellationSignal A signal to cancel the operation in progress, or null if none. + * If {@code null} then the provider is free to define the sort order. + * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. - * @return a Cursor or null. + * @return a Cursor or {@code null}. */ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, @@ -668,19 +756,37 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * to retrieve the MIME type for a URI when dispatching intents. * * @param uri the URI to query. - * @return a MIME type string, or null if there is no type. + * @return a MIME type string, or {@code null} if there is no type. */ public abstract String getType(Uri uri); /** + * @hide + * Implementation when a caller has performed an insert on the content + * provider, but that call has been rejected for the operation given + * to {@link #setAppOps(int, int)}. The default implementation simply + * returns a dummy URI that is the base URI with a 0 path element + * appended. + */ + public Uri rejectInsert(Uri uri, ContentValues values) { + // If not allowed, we need to return some reasonable URI. Maybe the + // content provider should be responsible for this, but for now we + // will just return the base URI with a dummy '0' tagged on to it. + // You shouldn't be able to read if you can't write, anyway, so it + // shouldn't matter much what is returned. + return uri.buildUpon().appendPath("0").build(); + } + + /** * Implement this to handle requests to insert a new row. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. - * @param uri The content:// URI of the insertion request. + * @param uri The content:// URI of the insertion request. This must not be {@code null}. * @param values A set of column_name/value pairs to add to the database. + * This must not be {@code null}. * @return The URI for the newly inserted item. */ public abstract Uri insert(Uri uri, ContentValues values); @@ -697,6 +803,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The content:// URI of the insertion request. * @param values An array of sets of column_name/value pairs to add to the database. + * This must not be {@code null}. * @return The number of values that were inserted. */ public int bulkInsert(Uri uri, ContentValues[] values) { @@ -741,8 +848,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The URI to query. This can potentially have a record ID if this * is an update request for a specific record. - * @param values A Bundle mapping from column names to new column values (NULL is a - * valid value). + * @param values A set of column_name/value pairs to update in the database. + * This must not be {@code null}. * @param selection An optional filter to match rows to update. * @return the number of rows affected. */ @@ -764,6 +871,18 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * their responsibility to close it when done. That is, the implementation * of this method should create a new ParcelFileDescriptor for each call. * + * <p class="note">For use in Intents, you will want to implement {@link #getType} + * to return the appropriate MIME type for the data returned here with + * the same URI. This will allow intent resolution to automatically determine the data MIME + * type and select the appropriate matching targets as part of its operation.</p> + * + * <p class="note">For better interoperability with other applications, it is recommended + * that for any URIs that can be opened, you also support queries on them + * containing at least the columns specified by {@link android.provider.OpenableColumns}. + * You may also want to support other common columns if you have additional meta-data + * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED} + * in {@link android.provider.MediaStore.MediaColumns}.</p> + * * @param uri The URI whose file is to be opened. * @param mode Access mode for the file. May be "r" for read-only access, * "rw" for read and write access, or "rwt" for read and write access @@ -779,6 +898,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @see #openAssetFile(Uri, String) * @see #openFileHelper(Uri, String) + * @see #getType(android.net.Uri) */ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { @@ -806,6 +926,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with * applications that can not handle sub-sections of files.</p> * + * <p class="note">For use in Intents, you will want to implement {@link #getType} + * to return the appropriate MIME type for the data returned here with + * the same URI. This will allow intent resolution to automatically determine the data MIME + * type and select the appropriate matching targets as part of its operation.</p> + * + * <p class="note">For better interoperability with other applications, it is recommended + * that for any URIs that can be opened, you also support queries on them + * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p> + * * @param uri The URI whose file is to be opened. * @param mode Access mode for the file. May be "r" for read-only access, * "w" for write-only access (erasing whatever data is currently in @@ -823,6 +952,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @see #openFile(Uri, String) * @see #openFileHelper(Uri, String) + * @see #getType(android.net.Uri) */ public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { @@ -875,7 +1005,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { /** * Called by a client to determine the types of data streams that this * content provider supports for the given URI. The default implementation - * returns null, meaning no types. If your content provider stores data + * returns {@code null}, meaning no types. If your content provider stores data * of a particular type, return that MIME type if it matches the given * mimeTypeFilter. If it can perform type conversions, return an array * of all supported MIME types that match mimeTypeFilter. @@ -883,7 +1013,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be * a pattern, such as *\/* to retrieve all possible data types. - * @return Returns null if there are no possible data streams for the + * @return Returns {@code null} if there are no possible data streams for the * given mimeTypeFilter. Otherwise returns an array of all available * concrete MIME types. * @@ -902,12 +1032,19 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * perform data conversions to generate data of the desired type. * * <p>The default implementation compares the given mimeType against the - * result of {@link #getType(Uri)} and, if the match, simple calls + * result of {@link #getType(Uri)} and, if they match, simply calls * {@link #openAssetFile(Uri, String)}. * * <p>See {@link ClipData} for examples of the use and implementation * of this method. * + * <p class="note">For better interoperability with other applications, it is recommended + * that for any URIs that can be opened, you also support queries on them + * containing at least the columns specified by {@link android.provider.OpenableColumns}. + * You may also want to support other common columns if you have additional meta-data + * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED} + * in {@link android.provider.MediaStore.MediaColumns}.</p> + * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be * a pattern, such as *\/*, if the caller does not have specific type @@ -1087,14 +1224,23 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } /** + * @hide + * Front-end to {@link #call(String, String, android.os.Bundle)} that provides the name + * of the calling package. + */ + public Bundle callFromPackage(String callingPackag, String method, String arg, Bundle extras) { + return call(method, arg, extras); + } + + /** * Call a provider-defined method. This can be used to implement * interfaces that are cheaper and/or unnatural for a table-like * model. * - * @param method method name to call. Opaque to framework, but should not be null. - * @param arg provider-defined String argument. May be null. - * @param extras provider-defined Bundle argument. May be null. - * @return provider-defined return value. May be null. Null is also + * @param method method name to call. Opaque to framework, but should not be {@code null}. + * @param arg provider-defined String argument. May be {@code null}. + * @param extras provider-defined Bundle argument. May be {@code null}. + * @return provider-defined return value. May be {@code null}, which is also * the default for providers which don't implement any call methods. */ public Bundle call(String method, String arg, Bundle extras) { @@ -1132,12 +1278,10 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * Print the Provider's state into the given stream. This gets invoked if * you run "adb shell dumpsys activity provider <provider_component_name>". * - * @param prefix Desired prefix to prepend at each line of output. * @param fd The raw file descriptor that the dump is being sent to. * @param writer The PrintWriter to which you should dump your state. This will be * closed for you after you return. * @param args additional arguments to the dump request. - * @hide */ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.println("nothing to dump"); diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 204f963..8dffac7 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -45,6 +45,7 @@ import java.util.ArrayList; public class ContentProviderClient { private final IContentProvider mContentProvider; private final ContentResolver mContentResolver; + private final String mPackageName; private final boolean mStable; private boolean mReleased; @@ -55,6 +56,7 @@ public class ContentProviderClient { IContentProvider contentProvider, boolean stable) { mContentProvider = contentProvider; mContentResolver = contentResolver; + mPackageName = contentResolver.mPackageName; mStable = stable; } @@ -81,8 +83,8 @@ public class ContentProviderClient { cancellationSignal.setRemote(remoteCancellationSignal); } try { - return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder, - remoteCancellationSignal); + return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs, + sortOrder, remoteCancellationSignal); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -119,7 +121,7 @@ public class ContentProviderClient { public Uri insert(Uri url, ContentValues initialValues) throws RemoteException { try { - return mContentProvider.insert(url, initialValues); + return mContentProvider.insert(mPackageName, url, initialValues); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -131,7 +133,7 @@ public class ContentProviderClient { /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */ public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException { try { - return mContentProvider.bulkInsert(url, initialValues); + return mContentProvider.bulkInsert(mPackageName, url, initialValues); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -144,7 +146,7 @@ public class ContentProviderClient { public int delete(Uri url, String selection, String[] selectionArgs) throws RemoteException { try { - return mContentProvider.delete(url, selection, selectionArgs); + return mContentProvider.delete(mPackageName, url, selection, selectionArgs); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -157,7 +159,7 @@ public class ContentProviderClient { public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) throws RemoteException { try { - return mContentProvider.update(url, values, selection, selectionArgs); + return mContentProvider.update(mPackageName, url, values, selection, selectionArgs); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -176,7 +178,7 @@ public class ContentProviderClient { public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException, FileNotFoundException { try { - return mContentProvider.openFile(url, mode); + return mContentProvider.openFile(mPackageName, url, mode); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -195,7 +197,7 @@ public class ContentProviderClient { public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException, FileNotFoundException { try { - return mContentProvider.openAssetFile(url, mode); + return mContentProvider.openAssetFile(mPackageName, url, mode); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -209,7 +211,7 @@ public class ContentProviderClient { String mimeType, Bundle opts) throws RemoteException, FileNotFoundException { try { - return mContentProvider.openTypedAssetFile(uri, mimeType, opts); + return mContentProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -222,7 +224,7 @@ public class ContentProviderClient { public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { try { - return mContentProvider.applyBatch(operations); + return mContentProvider.applyBatch(mPackageName, operations); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -235,7 +237,7 @@ public class ContentProviderClient { public Bundle call(String method, String arg, Bundle extras) throws RemoteException { try { - return mContentProvider.call(method, arg, extras); + return mContentProvider.call(mPackageName, method, arg, extras); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 550a1c9..6f822c1 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -81,6 +81,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); // String[] projection @@ -110,16 +111,24 @@ abstract public class ContentProviderNative extends Binder implements IContentPr ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface( data.readStrongBinder()); - Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder, - cancellationSignal); + Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs, + sortOrder, cancellationSignal); if (cursor != null) { - CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor( - cursor, observer, getProviderName()); - BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor(); - - reply.writeNoException(); - reply.writeInt(1); - d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + try { + CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor( + cursor, observer, getProviderName()); + BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor(); + cursor = null; + + reply.writeNoException(); + reply.writeInt(1); + d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } finally { + // Close cursor if an exception was thrown while constructing the adaptor. + if (cursor != null) { + cursor.close(); + } + } } else { reply.writeNoException(); reply.writeInt(0); @@ -142,10 +151,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case INSERT_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); - Uri out = insert(url, values); + Uri out = insert(callingPkg, url, values); reply.writeNoException(); Uri.writeToParcel(reply, out); return true; @@ -154,10 +164,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case BULK_INSERT_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues[] values = data.createTypedArray(ContentValues.CREATOR); - int count = bulkInsert(url, values); + int count = bulkInsert(callingPkg, url, values); reply.writeNoException(); reply.writeInt(count); return true; @@ -166,13 +177,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case APPLY_BATCH_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); final int numOperations = data.readInt(); final ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(numOperations); for (int i = 0; i < numOperations; i++) { operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data)); } - final ContentProviderResult[] results = applyBatch(operations); + final ContentProviderResult[] results = applyBatch(callingPkg, operations); reply.writeNoException(); reply.writeTypedArray(results, 0); return true; @@ -181,11 +193,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case DELETE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String selection = data.readString(); String[] selectionArgs = data.readStringArray(); - int count = delete(url, selection, selectionArgs); + int count = delete(callingPkg, url, selection, selectionArgs); reply.writeNoException(); reply.writeInt(count); @@ -195,12 +208,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case UPDATE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); String selection = data.readString(); String[] selectionArgs = data.readStringArray(); - int count = update(url, values, selection, selectionArgs); + int count = update(callingPkg, url, values, selection, selectionArgs); reply.writeNoException(); reply.writeInt(count); @@ -210,11 +224,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case OPEN_FILE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String mode = data.readString(); ParcelFileDescriptor fd; - fd = openFile(url, mode); + fd = openFile(callingPkg, url, mode); reply.writeNoException(); if (fd != null) { reply.writeInt(1); @@ -229,11 +244,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case OPEN_ASSET_FILE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String mode = data.readString(); AssetFileDescriptor fd; - fd = openAssetFile(url, mode); + fd = openAssetFile(callingPkg, url, mode); reply.writeNoException(); if (fd != null) { reply.writeInt(1); @@ -249,11 +265,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); String method = data.readString(); String stringArg = data.readString(); Bundle args = data.readBundle(); - Bundle responseBundle = call(method, stringArg, args); + Bundle responseBundle = call(callingPkg, method, stringArg, args); reply.writeNoException(); reply.writeBundle(responseBundle); @@ -275,12 +292,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case OPEN_TYPED_ASSET_FILE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String mimeType = data.readString(); Bundle opts = data.readBundle(); AssetFileDescriptor fd; - fd = openTypedAssetFile(url, mimeType, opts); + fd = openTypedAssetFile(callingPkg, url, mimeType, opts); reply.writeNoException(); if (fd != null) { reply.writeInt(1); @@ -329,7 +347,7 @@ final class ContentProviderProxy implements IContentProvider return mRemote; } - public Cursor query(Uri url, String[] projection, String selection, + public Cursor query(String callingPkg, Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException { BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); @@ -338,6 +356,7 @@ final class ContentProviderProxy implements IContentProvider try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); int length = 0; if (projection != null) { @@ -405,13 +424,14 @@ final class ContentProviderProxy implements IContentProvider } } - public Uri insert(Uri url, ContentValues values) throws RemoteException + public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); values.writeToParcel(data, 0); @@ -426,12 +446,13 @@ final class ContentProviderProxy implements IContentProvider } } - public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException { + public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); data.writeTypedArray(values, 0); @@ -446,12 +467,14 @@ final class ContentProviderProxy implements IContentProvider } } - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) - throws RemoteException, OperationApplicationException { + public ContentProviderResult[] applyBatch(String callingPkg, + ArrayList<ContentProviderOperation> operations) + throws RemoteException, OperationApplicationException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); data.writeInt(operations.size()); for (ContentProviderOperation operation : operations) { operation.writeToParcel(data, 0); @@ -468,13 +491,14 @@ final class ContentProviderProxy implements IContentProvider } } - public int delete(Uri url, String selection, String[] selectionArgs) + public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); data.writeString(selection); data.writeStringArray(selectionArgs); @@ -490,13 +514,14 @@ final class ContentProviderProxy implements IContentProvider } } - public int update(Uri url, ContentValues values, String selection, + public int update(String callingPkg, Uri url, ContentValues values, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); values.writeToParcel(data, 0); data.writeString(selection); @@ -513,13 +538,14 @@ final class ContentProviderProxy implements IContentProvider } } - public ParcelFileDescriptor openFile(Uri url, String mode) + public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); data.writeString(mode); @@ -535,13 +561,14 @@ final class ContentProviderProxy implements IContentProvider } } - public AssetFileDescriptor openAssetFile(Uri url, String mode) + public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); data.writeString(mode); @@ -558,13 +585,14 @@ final class ContentProviderProxy implements IContentProvider } } - public Bundle call(String method, String request, Bundle args) + public Bundle call(String callingPkg, String method, String request, Bundle args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); data.writeString(method); data.writeString(request); data.writeBundle(args); @@ -601,13 +629,14 @@ final class ContentProviderProxy implements IContentProvider } } - public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) - throws RemoteException, FileNotFoundException { + public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType, + Bundle opts) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); + data.writeString(callingPkg); url.writeToParcel(data, 0); data.writeString(mimeType); data.writeBundle(opts); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index bde4d2b..fefd343 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -20,6 +20,7 @@ import dalvik.system.CloseGuard; import android.accounts.Account; import android.app.ActivityManagerNative; +import android.app.ActivityThread; import android.app.AppGlobals; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetFileDescriptor; @@ -116,6 +117,10 @@ public abstract class ContentResolver { */ public static final String SYNC_EXTRAS_INITIALIZE = "initialize"; + /** @hide */ + public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED = + new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED"); + public static final String SCHEME_CONTENT = "content"; public static final String SCHEME_ANDROID_RESOURCE = "android.resource"; public static final String SCHEME_FILE = "file"; @@ -169,6 +174,42 @@ public abstract class ContentResolver { /** @hide */ public static final int SYNC_ERROR_INTERNAL = 8; + private static final String[] SYNC_ERROR_NAMES = new String[] { + "already-in-progress", + "authentication-error", + "io-error", + "parse-error", + "conflict", + "too-many-deletions", + "too-many-retries", + "internal-error", + }; + + /** @hide */ + public static String syncErrorToString(int error) { + if (error < 1 || error > SYNC_ERROR_NAMES.length) { + return String.valueOf(error); + } + return SYNC_ERROR_NAMES[error - 1]; + } + + /** @hide */ + public static int syncErrorStringToInt(String error) { + for (int i = 0, n = SYNC_ERROR_NAMES.length; i < n; i++) { + if (SYNC_ERROR_NAMES[i].equals(error)) { + return i + 1; + } + } + if (error != null) { + try { + return Integer.parseInt(error); + } catch (NumberFormatException e) { + Log.d(TAG, "error parsing sync error: " + error); + } + } + return 0; + } + public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0; public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1; public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2; @@ -183,7 +224,8 @@ public abstract class ContentResolver { private final Random mRandom = new Random(); // guarded by itself public ContentResolver(Context context) { - mContext = context; + mContext = context != null ? context : ActivityThread.currentApplication(); + mPackageName = mContext.getBasePackageName(); } /** @hide */ @@ -358,6 +400,7 @@ public abstract class ContentResolver { return null; } IContentProvider stableProvider = null; + Cursor qCursor = null; try { long startTime = SystemClock.uptimeMillis(); @@ -367,9 +410,8 @@ public abstract class ContentResolver { remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } - Cursor qCursor; try { - qCursor = unstableProvider.query(uri, projection, + qCursor = unstableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable @@ -380,26 +422,32 @@ public abstract class ContentResolver { if (stableProvider == null) { return null; } - qCursor = stableProvider.query(uri, projection, + qCursor = stableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } if (qCursor == null) { return null; } - // force query execution + + // Force query execution. Might fail and throw a runtime exception here. qCursor.getCount(); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); - // Wrap the cursor object into CursorWrapperInner object + + // Wrap the cursor object into CursorWrapperInner object. CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, stableProvider != null ? stableProvider : acquireProvider(uri)); stableProvider = null; + qCursor = null; return wrapper; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { + if (qCursor != null) { + qCursor.close(); + } if (unstableProvider != null) { releaseUnstableProvider(unstableProvider); } @@ -622,7 +670,7 @@ public abstract class ContentResolver { try { try { - fd = unstableProvider.openAssetFile(uri, mode); + fd = unstableProvider.openAssetFile(mPackageName, uri, mode); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -636,7 +684,7 @@ public abstract class ContentResolver { if (stableProvider == null) { throw new FileNotFoundException("No content provider: " + uri); } - fd = stableProvider.openAssetFile(uri, mode); + fd = stableProvider.openAssetFile(mPackageName, uri, mode); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -714,7 +762,7 @@ public abstract class ContentResolver { try { try { - fd = unstableProvider.openTypedAssetFile(uri, mimeType, opts); + fd = unstableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -728,7 +776,7 @@ public abstract class ContentResolver { if (stableProvider == null) { throw new FileNotFoundException("No content provider: " + uri); } - fd = stableProvider.openTypedAssetFile(uri, mimeType, opts); + fd = stableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -863,7 +911,7 @@ public abstract class ContentResolver { } try { long startTime = SystemClock.uptimeMillis(); - Uri createdRow = provider.insert(url, values); + Uri createdRow = provider.insert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); return createdRow; @@ -924,7 +972,7 @@ public abstract class ContentResolver { } try { long startTime = SystemClock.uptimeMillis(); - int rowsCreated = provider.bulkInsert(url, values); + int rowsCreated = provider.bulkInsert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */); return rowsCreated; @@ -955,7 +1003,7 @@ public abstract class ContentResolver { } try { long startTime = SystemClock.uptimeMillis(); - int rowsDeleted = provider.delete(url, where, selectionArgs); + int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "delete", where); return rowsDeleted; @@ -989,7 +1037,7 @@ public abstract class ContentResolver { } try { long startTime = SystemClock.uptimeMillis(); - int rowsUpdated = provider.update(uri, values, where, selectionArgs); + int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, uri, "update", where); return rowsUpdated; @@ -1028,7 +1076,7 @@ public abstract class ContentResolver { throw new IllegalArgumentException("Unknown URI " + uri); } try { - return provider.call(method, arg, extras); + return provider.call(mPackageName, method, arg, extras); } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -1926,7 +1974,13 @@ public abstract class ContentResolver { return sContentService; } + /** @hide */ + public String getPackageName() { + return mPackageName; + } + private static IContentService sContentService; private final Context mContext; + final String mPackageName; private static final String TAG = "ContentResolver"; } diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java deleted file mode 100644 index 4512e82..0000000 --- a/core/java/android/content/ContentService.java +++ /dev/null @@ -1,838 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.app.ActivityManager; -import android.database.IContentObserver; -import android.database.sqlite.SQLiteException; -import android.net.Uri; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.util.Log; -import android.util.SparseIntArray; -import android.Manifest; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * {@hide} - */ -public final class ContentService extends IContentService.Stub { - private static final String TAG = "ContentService"; - private Context mContext; - private boolean mFactoryTest; - private final ObserverNode mRootNode = new ObserverNode(""); - private SyncManager mSyncManager = null; - private final Object mSyncManagerLock = new Object(); - - private SyncManager getSyncManager() { - synchronized(mSyncManagerLock) { - try { - // Try to create the SyncManager, return null if it fails (e.g. the disk is full). - if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest); - } catch (SQLiteException e) { - Log.e(TAG, "Can't create SyncManager", e); - } - return mSyncManager; - } - } - - @Override - protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP, - "caller doesn't have the DUMP permission"); - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - if (mSyncManager == null) { - pw.println("No SyncManager created! (Disk full?)"); - } else { - mSyncManager.dump(fd, pw); - } - pw.println(); - pw.println("Observer tree:"); - synchronized (mRootNode) { - int[] counts = new int[2]; - final SparseIntArray pidCounts = new SparseIntArray(); - mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts); - pw.println(); - ArrayList<Integer> sorted = new ArrayList<Integer>(); - for (int i=0; i<pidCounts.size(); i++) { - sorted.add(pidCounts.keyAt(i)); - } - Collections.sort(sorted, new Comparator<Integer>() { - @Override - public int compare(Integer lhs, Integer rhs) { - int lc = pidCounts.get(lhs); - int rc = pidCounts.get(rhs); - if (lc < rc) { - return 1; - } else if (lc > rc) { - return -1; - } - return 0; - } - - }); - for (int i=0; i<sorted.size(); i++) { - int pid = sorted.get(i); - pw.print(" pid "); pw.print(pid); pw.print(": "); - pw.print(pidCounts.get(pid)); pw.println(" observers"); - } - pw.println(); - pw.print(" Total number of nodes: "); pw.println(counts[0]); - pw.print(" Total number of observers: "); pw.println(counts[1]); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (RuntimeException e) { - // The content service only throws security exceptions, so let's - // log all others. - if (!(e instanceof SecurityException)) { - Log.e(TAG, "Content Service Crash", e); - } - throw e; - } - } - - /*package*/ ContentService(Context context, boolean factoryTest) { - mContext = context; - mFactoryTest = factoryTest; - } - - public void systemReady() { - getSyncManager(); - } - - /** - * Register a content observer tied to a specific user's view of the provider. - * @param userHandle the user whose view of the provider is to be observed. May be - * the calling user without requiring any permission, otherwise the caller needs to - * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and - * USER_CURRENT are properly handled; all other pseudousers are forbidden. - */ - @Override - public void registerContentObserver(Uri uri, boolean notifyForDescendants, - IContentObserver observer, int userHandle) { - if (observer == null || uri == null) { - throw new IllegalArgumentException("You must pass a valid uri and observer"); - } - - final int callingUser = UserHandle.getCallingUserId(); - if (callingUser != userHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "no permission to observe other users' provider view"); - } - - if (userHandle < 0) { - if (userHandle == UserHandle.USER_CURRENT) { - userHandle = ActivityManager.getCurrentUser(); - } else if (userHandle != UserHandle.USER_ALL) { - throw new InvalidParameterException("Bad user handle for registerContentObserver: " - + userHandle); - } - } - - synchronized (mRootNode) { - mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, - Binder.getCallingUid(), Binder.getCallingPid(), userHandle); - if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + - " with notifyForDescendants " + notifyForDescendants); - } - } - - public void registerContentObserver(Uri uri, boolean notifyForDescendants, - IContentObserver observer) { - registerContentObserver(uri, notifyForDescendants, observer, - UserHandle.getCallingUserId()); - } - - public void unregisterContentObserver(IContentObserver observer) { - if (observer == null) { - throw new IllegalArgumentException("You must pass a valid observer"); - } - synchronized (mRootNode) { - mRootNode.removeObserverLocked(observer); - if (false) Log.v(TAG, "Unregistered observer " + observer); - } - } - - /** - * Notify observers of a particular user's view of the provider. - * @param userHandle the user whose view of the provider is to be notified. May be - * the calling user without requiring any permission, otherwise the caller needs to - * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and - * USER_CURRENT are properly interpreted; no other pseudousers are allowed. - */ - @Override - public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork, - int userHandle) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle - + " from observer " + observer + ", syncToNetwork " + syncToNetwork); - } - - // Notify for any user other than the caller's own requires permission. - final int callingUserHandle = UserHandle.getCallingUserId(); - if (userHandle != callingUserHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "no permission to notify other users"); - } - - // We passed the permission check; resolve pseudouser targets as appropriate - if (userHandle < 0) { - if (userHandle == UserHandle.USER_CURRENT) { - userHandle = ActivityManager.getCurrentUser(); - } else if (userHandle != UserHandle.USER_ALL) { - throw new InvalidParameterException("Bad user handle for notifyChange: " - + userHandle); - } - } - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); - synchronized (mRootNode) { - mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, - userHandle, calls); - } - final int numCalls = calls.size(); - for (int i=0; i<numCalls; i++) { - ObserverCall oc = calls.get(i); - try { - oc.mObserver.onChange(oc.mSelfChange, uri); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri); - } - } catch (RemoteException ex) { - synchronized (mRootNode) { - Log.w(TAG, "Found dead observer, removing"); - IBinder binder = oc.mObserver.asBinder(); - final ArrayList<ObserverNode.ObserverEntry> list - = oc.mNode.mObservers; - int numList = list.size(); - for (int j=0; j<numList; j++) { - ObserverNode.ObserverEntry oe = list.get(j); - if (oe.observer.asBinder() == binder) { - list.remove(j); - j--; - numList--; - } - } - } - } - } - if (syncToNetwork) { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, - uri.getAuthority()); - } - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork) { - notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, - UserHandle.getCallingUserId()); - } - - /** - * Hide this class since it is not part of api, - * but current unittest framework requires it to be public - * @hide - * - */ - public static final class ObserverCall { - final ObserverNode mNode; - final IContentObserver mObserver; - final boolean mSelfChange; - - ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) { - mNode = node; - mObserver = observer; - mSelfChange = selfChange; - } - } - - public void requestSync(Account account, String authority, Bundle extras) { - ContentResolver.validateSyncExtrasBundle(extras); - int userId = UserHandle.getCallingUserId(); - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.scheduleSync(account, userId, authority, extras, 0 /* no delay */, - false /* onlyThoseWithUnkownSyncableState */); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** - * Clear all scheduled sync operations that match the uri and cancel the active sync - * if they match the authority and account, if they are present. - * @param account filter the pending and active syncs to cancel using this account - * @param authority filter the pending and active syncs to cancel using this authority - */ - public void cancelSync(Account account, String authority) { - int userId = UserHandle.getCallingUserId(); - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.clearScheduledSyncOperations(account, userId, authority); - syncManager.cancelActiveSync(account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** - * Get information about the SyncAdapters that are known to the system. - * @return an array of SyncAdapters that have registered with the system - */ - public SyncAdapterType[] getSyncAdapterTypes() { - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - final int userId = UserHandle.getCallingUserId(); - final long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - return syncManager.getSyncAdapterTypes(userId); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean getSyncAutomatically(Account account, String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getSyncAutomatically( - account, userId, providerName); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public void setSyncAutomatically(Account account, String providerName, boolean sync) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.getSyncStorageEngine().setSyncAutomatically( - account, userId, providerName, sync); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void addPeriodicSync(Account account, String authority, Bundle extras, - long pollFrequency) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - getSyncManager().getSyncStorageEngine().addPeriodicSync( - account, userId, authority, extras, pollFrequency); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void removePeriodicSync(Account account, String authority, Bundle extras) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority, - extras); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( - account, userId, providerName); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public int getIsSyncable(Account account, String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getIsSyncable( - account, userId, providerName); - } - } finally { - restoreCallingIdentity(identityToken); - } - return -1; - } - - public void setIsSyncable(Account account, String providerName, int syncable) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.getSyncStorageEngine().setIsSyncable( - account, userId, providerName, syncable); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean getMasterSyncAutomatically() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public void setMasterSyncAutomatically(boolean flag) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean isSyncActive(Account account, String authority) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().isSyncActive( - account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public List<SyncInfo> getCurrentSyncs() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public SyncStatusInfo getSyncStatus(Account account, String authority) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( - account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - return null; - } - - public boolean isSyncPending(Account account, String authority) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null && callback != null) { - syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void removeStatusChangeListener(ISyncStatusObserver callback) { - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null && callback != null) { - syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public static ContentService main(Context context, boolean factoryTest) { - ContentService service = new ContentService(context, factoryTest); - ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); - return service; - } - - /** - * Hide this class since it is not part of api, - * but current unittest framework requires it to be public - * @hide - */ - public static final class ObserverNode { - private class ObserverEntry implements IBinder.DeathRecipient { - public final IContentObserver observer; - public final int uid; - public final int pid; - public final boolean notifyForDescendants; - private final int userHandle; - private final Object observersLock; - - public ObserverEntry(IContentObserver o, boolean n, Object observersLock, - int _uid, int _pid, int _userHandle) { - this.observersLock = observersLock; - observer = o; - uid = _uid; - pid = _pid; - userHandle = _userHandle; - notifyForDescendants = n; - try { - observer.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - binderDied(); - } - } - - public void binderDied() { - synchronized (observersLock) { - removeObserverLocked(observer); - } - } - - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, - String name, String prefix, SparseIntArray pidCounts) { - pidCounts.put(pid, pidCounts.get(pid)+1); - pw.print(prefix); pw.print(name); pw.print(": pid="); - pw.print(pid); pw.print(" uid="); - pw.print(uid); pw.print(" user="); - pw.print(userHandle); pw.print(" target="); - pw.println(Integer.toHexString(System.identityHashCode( - observer != null ? observer.asBinder() : null))); - } - } - - public static final int INSERT_TYPE = 0; - public static final int UPDATE_TYPE = 1; - public static final int DELETE_TYPE = 2; - - private String mName; - private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); - private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); - - public ObserverNode(String name) { - mName = name; - } - - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, - String name, String prefix, int[] counts, SparseIntArray pidCounts) { - String innerName = null; - if (mObservers.size() > 0) { - if ("".equals(name)) { - innerName = mName; - } else { - innerName = name + "/" + mName; - } - for (int i=0; i<mObservers.size(); i++) { - counts[1]++; - mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix, - pidCounts); - } - } - if (mChildren.size() > 0) { - if (innerName == null) { - if ("".equals(name)) { - innerName = mName; - } else { - innerName = name + "/" + mName; - } - } - for (int i=0; i<mChildren.size(); i++) { - counts[0]++; - mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix, - counts, pidCounts); - } - } - } - - private String getUriSegment(Uri uri, int index) { - if (uri != null) { - if (index == 0) { - return uri.getAuthority(); - } else { - return uri.getPathSegments().get(index - 1); - } - } else { - return null; - } - } - - private int countUriSegments(Uri uri) { - if (uri == null) { - return 0; - } - return uri.getPathSegments().size() + 1; - } - - // Invariant: userHandle is either a hard user number or is USER_ALL - public void addObserverLocked(Uri uri, IContentObserver observer, - boolean notifyForDescendants, Object observersLock, - int uid, int pid, int userHandle) { - addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, - uid, pid, userHandle); - } - - private void addObserverLocked(Uri uri, int index, IContentObserver observer, - boolean notifyForDescendants, Object observersLock, - int uid, int pid, int userHandle) { - // If this is the leaf node add the observer - if (index == countUriSegments(uri)) { - mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, - uid, pid, userHandle)); - return; - } - - // Look to see if the proper child already exists - String segment = getUriSegment(uri, index); - if (segment == null) { - throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer"); - } - int N = mChildren.size(); - for (int i = 0; i < N; i++) { - ObserverNode node = mChildren.get(i); - if (node.mName.equals(segment)) { - node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, - observersLock, uid, pid, userHandle); - return; - } - } - - // No child found, create one - ObserverNode node = new ObserverNode(segment); - mChildren.add(node); - node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, - observersLock, uid, pid, userHandle); - } - - public boolean removeObserverLocked(IContentObserver observer) { - int size = mChildren.size(); - for (int i = 0; i < size; i++) { - boolean empty = mChildren.get(i).removeObserverLocked(observer); - if (empty) { - mChildren.remove(i); - i--; - size--; - } - } - - IBinder observerBinder = observer.asBinder(); - size = mObservers.size(); - for (int i = 0; i < size; i++) { - ObserverEntry entry = mObservers.get(i); - if (entry.observer.asBinder() == observerBinder) { - mObservers.remove(i); - // We no longer need to listen for death notifications. Remove it. - observerBinder.unlinkToDeath(entry, 0); - break; - } - } - - if (mChildren.size() == 0 && mObservers.size() == 0) { - return true; - } - return false; - } - - private void collectMyObserversLocked(boolean leaf, IContentObserver observer, - boolean observerWantsSelfNotifications, int targetUserHandle, - ArrayList<ObserverCall> calls) { - int N = mObservers.size(); - IBinder observerBinder = observer == null ? null : observer.asBinder(); - for (int i = 0; i < N; i++) { - ObserverEntry entry = mObservers.get(i); - - // Don't notify the observer if it sent the notification and isn't interested - // in self notifications - boolean selfChange = (entry.observer.asBinder() == observerBinder); - if (selfChange && !observerWantsSelfNotifications) { - continue; - } - - // Does this observer match the target user? - if (targetUserHandle == UserHandle.USER_ALL - || entry.userHandle == UserHandle.USER_ALL - || targetUserHandle == entry.userHandle) { - // Make sure the observer is interested in the notification - if (leaf || (!leaf && entry.notifyForDescendants)) { - calls.add(new ObserverCall(this, entry.observer, selfChange)); - } - } - } - } - - /** - * targetUserHandle is either a hard user handle or is USER_ALL - */ - public void collectObserversLocked(Uri uri, int index, IContentObserver observer, - boolean observerWantsSelfNotifications, int targetUserHandle, - ArrayList<ObserverCall> calls) { - String segment = null; - int segmentCount = countUriSegments(uri); - if (index >= segmentCount) { - // This is the leaf node, notify all observers - collectMyObserversLocked(true, observer, observerWantsSelfNotifications, - targetUserHandle, calls); - } else if (index < segmentCount){ - segment = getUriSegment(uri, index); - // Notify any observers at this level who are interested in descendants - collectMyObserversLocked(false, observer, observerWantsSelfNotifications, - targetUserHandle, calls); - } - - int N = mChildren.size(); - for (int i = 0; i < N; i++) { - ObserverNode node = mChildren.get(i); - if (segment == null || node.mName.equals(segment)) { - // We found the child, - node.collectObserversLocked(uri, index + 1, - observer, observerWantsSelfNotifications, targetUserHandle, calls); - if (segment != null) { - break; - } - } - } - } - } -} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7aa2507..8a9eed2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -418,6 +418,9 @@ public abstract class Context { /** Return the name of this application's package. */ public abstract String getPackageName(); + /** @hide Return the name of the base context this context is derived from. */ + public abstract String getBasePackageName(); + /** Return the full application info for this context's package. */ public abstract ApplicationInfo getApplicationInfo(); @@ -1135,6 +1138,14 @@ public abstract class Context { String receiverPermission); /** + * Like {@link #sendBroadcast(Intent, String)}, but also allows specification + * of an assocated app op as per {@link android.app.AppOpsManager}. + * @hide + */ + public abstract void sendBroadcast(Intent intent, + String receiverPermission, int appOp); + + /** * Broadcast the given intent to all interested BroadcastReceivers, delivering * them one at a time to allow more preferred receivers to consume the * broadcast before it is delivered to less preferred receivers. This @@ -1205,6 +1216,17 @@ public abstract class Context { Bundle initialExtras); /** + * Like {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, + * int, String, android.os.Bundle)}, but also allows specification + * of an assocated app op as per {@link android.app.AppOpsManager}. + * @hide + */ + public abstract void sendOrderedBroadcast(Intent intent, + String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras); + + /** * Version of {@link #sendBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications * that are not pre-installed on the system image. Using it requires holding @@ -1677,7 +1699,7 @@ public abstract class Context { * argument for use by system server and other multi-user aware code. * @hide */ - public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) { + public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user) { throw new RuntimeException("Not implemented. Must override in a subclass."); } @@ -1993,17 +2015,6 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link - * android.net.ThrottleManager} for handling management of - * throttling. - * - * @hide - * @see #getSystemService - * @see android.net.ThrottleManager - */ - public static final String THROTTLE_SERVICE = "throttle"; - - /** - * Use with {@link #getSystemService} to retrieve a {@link * android.os.IUpdateLock} for managing runtime sequences that * must not be interrupted by headless OTA application or similar. * @@ -2255,6 +2266,18 @@ public abstract class Context { public static final String USER_SERVICE = "user"; /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.app.AppOpsManager} for tracking application operations + * on the device. + * + * @see #getSystemService + * @see android.app.AppOpsManager + * + * @hide + */ + public static final String APP_OPS_SERVICE = "appops"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * @@ -2668,6 +2691,14 @@ public abstract class Context { throws PackageManager.NameNotFoundException; /** + * Get the userId associated with this context + * @return user id + * + * @hide + */ + public abstract int getUserId(); + + /** * Return a new Context object for the current Context but whose resources * are adjusted to match the given Configuration. Each call to this method * returns a new instance of a Context object; Context objects are not diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 84ad667..2f1bf8c 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -135,6 +135,12 @@ public class ContextWrapper extends Context { return mBase.getPackageName(); } + /** @hide */ + @Override + public String getBasePackageName() { + return mBase.getBasePackageName(); + } + @Override public ApplicationInfo getApplicationInfo() { return mBase.getApplicationInfo(); @@ -343,6 +349,12 @@ public class ContextWrapper extends Context { mBase.sendBroadcast(intent, receiverPermission); } + /** @hide */ + @Override + public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { + mBase.sendBroadcast(intent, receiverPermission, appOp); + } + @Override public void sendOrderedBroadcast(Intent intent, String receiverPermission) { @@ -359,6 +371,17 @@ public class ContextWrapper extends Context { initialData, initialExtras); } + /** @hide */ + @Override + public void sendOrderedBroadcast( + Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + mBase.sendOrderedBroadcast(intent, receiverPermission, appOp, + resultReceiver, scheduler, initialCode, + initialData, initialExtras); + } + @Override public void sendBroadcastAsUser(Intent intent, UserHandle user) { mBase.sendBroadcastAsUser(intent, user); @@ -475,8 +498,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override - public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) { - return mBase.bindService(service, conn, flags, userHandle); + public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, + UserHandle user) { + return mBase.bindServiceAsUser(service, conn, flags, user); } @Override @@ -599,6 +623,12 @@ public class ContextWrapper extends Context { return mBase.createPackageContextAsUser(packageName, flags, user); } + /** @hide */ + @Override + public int getUserId() { + return mBase.getUserId(); + } + @Override public Context createConfigurationContext(Configuration overrideConfiguration) { return mBase.createConfigurationContext(overrideConfiguration); diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java index 9f7a104..4e89dec 100644 --- a/core/java/android/content/CursorLoader.java +++ b/core/java/android/content/CursorLoader.java @@ -65,9 +65,14 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder, mCancellationSignal); if (cursor != null) { - // Ensure the cursor window is filled - cursor.getCount(); - registerContentObserver(cursor, mObserver); + try { + // Ensure the cursor window is filled. + cursor.getCount(); + registerContentObserver(cursor, mObserver); + } catch (RuntimeException ex) { + cursor.close(); + throw ex; + } } return cursor; } finally { diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index eeba1e0..62b79f0 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -34,30 +34,33 @@ import java.util.ArrayList; * @hide */ public interface IContentProvider extends IInterface { - public Cursor query(Uri url, String[] projection, String selection, + public Cursor query(String callingPkg, Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException; public String getType(Uri url) throws RemoteException; - public Uri insert(Uri url, ContentValues initialValues) + public Uri insert(String callingPkg, Uri url, ContentValues initialValues) throws RemoteException; - public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException; - public int delete(Uri url, String selection, String[] selectionArgs) + public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues) throws RemoteException; - public int update(Uri url, ContentValues values, String selection, + public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) + throws RemoteException; + public int update(String callingPkg, Uri url, ContentValues values, String selection, String[] selectionArgs) throws RemoteException; - public ParcelFileDescriptor openFile(Uri url, String mode) + public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode) throws RemoteException, FileNotFoundException; - public AssetFileDescriptor openAssetFile(Uri url, String mode) + public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode) throws RemoteException, FileNotFoundException; - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) - throws RemoteException, OperationApplicationException; - public Bundle call(String method, String arg, Bundle extras) throws RemoteException; + public ContentProviderResult[] applyBatch(String callingPkg, + ArrayList<ContentProviderOperation> operations) + throws RemoteException, OperationApplicationException; + public Bundle call(String callingPkg, String method, String arg, Bundle extras) + throws RemoteException; public ICancellationSignal createCancellationSignal() throws RemoteException; // Data interchange. public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException; - public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) - throws RemoteException, FileNotFoundException; + public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType, + Bundle opts) throws RemoteException, FileNotFoundException; /* IPC constants */ static final String descriptor = "android.content.IContentProvider"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 89b1bbd..f8ff8d1 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -883,7 +883,7 @@ public class Intent implements Parcelable, Cloneable { * Activity Action: Allow the user to select a particular kind of data and * return it. This is different than {@link #ACTION_PICK} in that here we * just say what kind of data is desired, not a URI of existing data from - * which the user can pick. A ACTION_GET_CONTENT could allow the user to + * which the user can pick. An ACTION_GET_CONTENT could allow the user to * create the data as it runs (for example taking a picture or recording a * sound), let them browse over the web and download the desired data, * etc. @@ -917,12 +917,17 @@ public class Intent implements Parcelable, Cloneable { * from a remote server but not already on the local device (thus requiring * they be downloaded when opened). * <p> + * If the caller can handle multiple returned items (the user performing + * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} + * to indicate this. + * <p> * Input: {@link #getType} is the desired MIME type to retrieve. Note * that no URI is supplied in the intent, as there are no constraints on * where the returned data originally comes from. You may also include the * {@link #CATEGORY_OPENABLE} if you can only accept data that can be * opened as a stream. You may use {@link #EXTRA_LOCAL_ONLY} to limit content - * selection to local data. + * selection to local data. You may use {@link #EXTRA_ALLOW_MULTIPLE} to + * allow the user to select multiple items. * <p> * Output: The URI of the item that was picked. This must be a content: * URI so that any receiver can access it. @@ -1140,14 +1145,33 @@ public class Intent implements Parcelable, Cloneable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH"; + /** * Activity Action: Perform assist action. * <p> - * Input: nothing + * Input: {@link #EXTRA_ASSIST_PACKAGE} and {@link #EXTRA_ASSIST_CONTEXT} can provide + * additional optional contextual information about where the user was when they requested + * the assist. * Output: nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_ASSIST = "android.intent.action.ASSIST"; + + /** + * An optional field on {@link #ACTION_ASSIST} containing the name of the current + * foreground application package at the time the assist was invoked. + */ + public static final String EXTRA_ASSIST_PACKAGE + = "android.intent.extra.ASSIST_PACKAGE"; + + /** + * An optional field on {@link #ACTION_ASSIST} containing additional contextual + * information supplied by the current foreground app at the time of the assist + * request. This is a {@link Bundle} of additional data. + */ + public static final String EXTRA_ASSIST_CONTEXT + = "android.intent.extra.ASSIST_CONTEXT"; + /** * Activity Action: List all available applications * <p>Input: Nothing. @@ -1588,7 +1612,7 @@ public class Intent implements Parcelable, Cloneable { * <ul> * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. * <li> {@link #EXTRA_CHANGED_COMPONENT_NAME_LIST} containing the class name - * of the changed components. + * of the changed components (or the package name itself). * <li> {@link #EXTRA_DONT_KILL_APP} containing boolean field to override the * default action of restarting the application. * </ul> @@ -2295,6 +2319,61 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.DOCK_EVENT"; /** + * Broadcast Action: A broadcast when idle maintenance can be started. + * This means that the user is not interacting with the device and is + * not expected to do so soon. Typical use of the idle maintenance is + * to perform somehow expensive tasks that can be postponed at a moment + * when they will not degrade user experience. + * <p> + * <p class="note">In order to keep the device responsive in case of an + * unexpected user interaction, implementations of a maintenance task + * should be interruptible. In such a scenario a broadcast with action + * {@link #ACTION_IDLE_MAINTENANCE_END} will be sent. In other words, you + * should not do the maintenance work in + * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather start a + * maintenance service by {@link Context#startService(Intent)}. Also + * you should hold a wake lock while your maintenance service is running + * to prevent the device going to sleep. + * </p> + * <p> + * <p class="note">This is a protected intent that can only be sent by + * the system. + * </p> + * + * @see #ACTION_IDLE_MAINTENANCE_END + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_IDLE_MAINTENANCE_START = + "android.intent.action.ACTION_IDLE_MAINTENANCE_START"; + + /** + * Broadcast Action: A broadcast when idle maintenance should be stopped. + * This means that the user was not interacting with the device as a result + * of which a broadcast with action {@link #ACTION_IDLE_MAINTENANCE_START} + * was sent and now the user started interacting with the device. Typical + * use of the idle maintenance is to perform somehow expensive tasks that + * can be postponed at a moment when they will not degrade user experience. + * <p> + * <p class="note">In order to keep the device responsive in case of an + * unexpected user interaction, implementations of a maintenance task + * should be interruptible. Hence, on receiving a broadcast with this + * action, the maintenance task should be interrupted as soon as possible. + * In other words, you should not do the maintenance work in + * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather stop the + * maintenance service that was started on receiving of + * {@link #ACTION_IDLE_MAINTENANCE_START}.Also you should release the wake + * lock you acquired when your maintenance service started. + * </p> + * <p class="note">This is a protected intent that can only be sent + * by the system. + * + * @see #ACTION_IDLE_MAINTENANCE_START + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_IDLE_MAINTENANCE_END = + "android.intent.action.ACTION_IDLE_MAINTENANCE_END"; + + /** * Broadcast Action: a remote intent is to be broadcasted. * * A remote intent is used for remote RPC between devices. The remote intent @@ -2465,6 +2544,14 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK"; + /** + * Broadcast Action: This is broadcast when a user action should request the + * brightness setting dialog. + * @hide + */ + public static final String ACTION_SHOW_BRIGHTNESS_DIALOG = + "android.intent.action.SHOW_BRIGHTNESS_DIALOG"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -2586,7 +2673,8 @@ public class Intent implements Parcelable, Cloneable { public static final String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE"; /** * Used to indicate that a GET_CONTENT intent only wants URIs that can be opened with - * ContentResolver.openInputStream. Openable URIs must support the columns in OpenableColumns + * ContentResolver.openInputStream. Openable URIs must support the columns in + * {@link android.provider.OpenableColumns} * when queried, though it is allowable for those columns to be blank. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) @@ -2969,7 +3057,9 @@ public class Intent implements Parcelable, Cloneable { /** * This field is part of {@link android.content.Intent#ACTION_PACKAGE_CHANGED}, - * and contains a string array of all of the components that have changed. + * and contains a string array of all of the components that have changed. If + * the state of the overall package has changed, then it will contain an entry + * with the package name itself. */ public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list"; @@ -3024,6 +3114,17 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.LOCAL_ONLY"; /** + * Used to indicate that a {@link #ACTION_GET_CONTENT} intent can allow the + * user to select and return multiple items. This is a boolean extra; the default + * is false. If true, an implementation of ACTION_GET_CONTENT is allowed to + * present the user with a UI where they can pick multiple items that are all + * returned to the caller. When this happens, they should be returned as + * the {@link #getClipData()} part of the result Intent. + */ + public static final String EXTRA_ALLOW_MULTIPLE = + "android.intent.extra.ALLOW_MULTIPLE"; + + /** * The userHandle carried with broadcast intents related to addition, removal and switching of users * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}. * @hide diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 3b0d846..5e65b59 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -86,7 +86,8 @@ import java.util.Set; * <strong>data scheme+authority+path</strong> if specified) must match. * * <p><strong>Action</strong> matches if any of the given values match the - * Intent action, <em>or</em> if no actions were specified in the filter. + * Intent action; if the filter specifies no actions, then it will only match + * Intents that do not contain an action. * * <p><strong>Data Type</strong> matches if any of the given values match the * Intent type. The Intent diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 17813ec..513a556 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -79,6 +79,25 @@ public class PeriodicSync implements Parcelable { return account.equals(other.account) && authority.equals(other.authority) && period == other.period - && SyncStorageEngine.equals(extras, other.extras); + && syncExtrasEquals(extras, other.extras); + } + + /** {@hide} */ + public static boolean syncExtrasEquals(Bundle b1, Bundle b2) { + if (b1.size() != b2.size()) { + return false; + } + if (b1.isEmpty()) { + return true; + } + for (String key : b1.keySet()) { + if (!b2.containsKey(key)) { + return false; + } + if (!b1.get(key).equals(b2.get(key))) { + return false; + } + } + return true; } } diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 7b643a0..8bb3ee7 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -31,7 +31,7 @@ import java.io.IOException; * A cache of services that export the {@link android.content.ISyncAdapter} interface. * @hide */ -/* package private */ class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> { +public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> { private static final String TAG = "Account"; private static final String SERVICE_INTERFACE = "android.content.SyncAdapter"; @@ -39,7 +39,7 @@ import java.io.IOException; private static final String ATTRIBUTES_NAME = "sync-adapter"; private static final MySerializer sSerializer = new MySerializer(); - SyncAdaptersCache(Context context) { + public SyncAdaptersCache(Context context) { super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer); } diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java index abfe964..0284882 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -46,7 +46,7 @@ public class SyncInfo implements Parcelable { public final long startTime; /** @hide */ - SyncInfo(int authorityId, Account account, String authority, + public SyncInfo(int authorityId, Account account, String authority, long startTime) { this.authorityId = authorityId; this.account = account; diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java deleted file mode 100644 index e4b4b97..0000000 --- a/core/java/android/content/SyncManager.java +++ /dev/null @@ -1,2632 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.accounts.AccountAndUser; -import android.accounts.AccountManager; -import android.accounts.AccountManagerService; -import android.app.ActivityManager; -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.SyncStorageEngine.OnSyncRequestListener; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCacheListener; -import android.content.pm.ResolveInfo; -import android.content.pm.UserInfo; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.WorkSource; -import android.provider.Settings; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.EventLog; -import android.util.Log; -import android.util.Pair; -import android.util.Slog; - -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.IndentingPrintWriter; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; -import com.google.android.collect.Sets; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CountDownLatch; - -/** - * @hide - */ -public class SyncManager { - private static final String TAG = "SyncManager"; - - /** Delay a sync due to local changes this long. In milliseconds */ - private static final long LOCAL_SYNC_DELAY; - - /** - * If a sync takes longer than this and the sync queue is not empty then we will - * cancel it and add it back to the end of the sync queue. In milliseconds. - */ - private static final long MAX_TIME_PER_SYNC; - - static { - final boolean isLargeRAM = ActivityManager.isLargeRAM(); - int defaultMaxInitSyncs = isLargeRAM ? 5 : 2; - int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1; - MAX_SIMULTANEOUS_INITIALIZATION_SYNCS = - SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs); - MAX_SIMULTANEOUS_REGULAR_SYNCS = - SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs); - LOCAL_SYNC_DELAY = - SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */); - MAX_TIME_PER_SYNC = - SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */); - SYNC_NOTIFICATION_DELAY = - SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */); - } - - private static final long SYNC_NOTIFICATION_DELAY; - - /** - * When retrying a sync for the first time use this delay. After that - * the retry time will double until it reached MAX_SYNC_RETRY_TIME. - * In milliseconds. - */ - private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds - - /** - * Default the max sync retry time to this value. - */ - private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour - - /** - * How long to wait before retrying a sync that failed due to one already being in progress. - */ - private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10; - - private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000; - - private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*"; - private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; - private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; - - private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS; - private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS; - - private Context mContext; - - private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; - - // TODO: add better locking around mRunningAccounts - private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; - - volatile private PowerManager.WakeLock mHandleAlarmWakeLock; - volatile private PowerManager.WakeLock mSyncManagerWakeLock; - volatile private boolean mDataConnectionIsConnected = false; - volatile private boolean mStorageIsLow = false; - - private final NotificationManager mNotificationMgr; - private AlarmManager mAlarmService = null; - - private SyncStorageEngine mSyncStorageEngine; - - @GuardedBy("mSyncQueue") - private final SyncQueue mSyncQueue; - - protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); - - // set if the sync active indicator should be reported - private boolean mNeedSyncActiveNotification = false; - - private final PendingIntent mSyncAlarmIntent; - // Synchronized on "this". Instead of using this directly one should instead call - // its accessor, getConnManager(). - private ConnectivityManager mConnManagerDoNotUseDirectly; - - protected SyncAdaptersCache mSyncAdapters; - - private BroadcastReceiver mStorageIntentReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Internal storage is low."); - } - mStorageIsLow = true; - cancelActiveSync(null /* any account */, UserHandle.USER_ALL, - null /* any authority */); - } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Internal storage is ok."); - } - mStorageIsLow = false; - sendCheckAlarmsMessage(); - } - } - }; - - private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - mSyncHandler.onBootCompleted(); - } - }; - - private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - if (getConnectivityManager().getBackgroundDataSetting()) { - scheduleSync(null /* account */, UserHandle.USER_ALL, null /* authority */, - new Bundle(), 0 /* delay */, - false /* onlyThoseWithUnknownSyncableState */); - } - } - }; - - private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - updateRunningAccounts(); - - // Kick off sync for everyone, since this was a radical account change - scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, false); - } - }; - - private final PowerManager mPowerManager; - - // Use this as a random offset to seed all periodic syncs - private int mSyncRandomOffsetMillis; - - private final UserManager mUserManager; - - private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds - private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours - - private List<UserInfo> getAllUsers() { - return mUserManager.getUsers(); - } - - private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) { - boolean found = false; - for (int i = 0; i < accounts.length; i++) { - if (accounts[i].userId == userId - && accounts[i].account.equals(account)) { - found = true; - break; - } - } - return found; - } - - public void updateRunningAccounts() { - mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); - - if (mBootCompleted) { - doDatabaseCleanup(); - } - - for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!containsAccountAndUser(mRunningAccounts, - currentSyncContext.mSyncOperation.account, - currentSyncContext.mSyncOperation.userId)) { - Log.d(TAG, "canceling sync since the account is no longer running"); - sendSyncFinishedOrCanceledMessage(currentSyncContext, - null /* no result since this is a cancel */); - } - } - - // we must do this since we don't bother scheduling alarms when - // the accounts are not set yet - sendCheckAlarmsMessage(); - } - - private void doDatabaseCleanup() { - for (UserInfo user : mUserManager.getUsers(true)) { - // Skip any partially created/removed users - if (user.partial) continue; - Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id); - mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); - } - } - - private BroadcastReceiver mConnectivityIntentReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - final boolean wasConnected = mDataConnectionIsConnected; - - // don't use the intent to figure out if network is connected, just check - // ConnectivityManager directly. - mDataConnectionIsConnected = readDataConnectionState(); - if (mDataConnectionIsConnected) { - if (!wasConnected) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Reconnection detected: clearing all backoffs"); - } - mSyncStorageEngine.clearAllBackoffs(mSyncQueue); - } - sendCheckAlarmsMessage(); - } - } - }; - - private boolean readDataConnectionState() { - NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo(); - return (networkInfo != null) && networkInfo.isConnected(); - } - - private BroadcastReceiver mShutdownIntentReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - Log.w(TAG, "Writing sync state before shutdown..."); - getSyncStorageEngine().writeAllState(); - } - }; - - private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - if (userId == UserHandle.USER_NULL) return; - - if (Intent.ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(userId); - } else if (Intent.ACTION_USER_STARTING.equals(action)) { - onUserStarting(userId); - } else if (Intent.ACTION_USER_STOPPING.equals(action)) { - onUserStopping(userId); - } - } - }; - - private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM"; - private final SyncHandler mSyncHandler; - - private volatile boolean mBootCompleted = false; - - private ConnectivityManager getConnectivityManager() { - synchronized (this) { - if (mConnManagerDoNotUseDirectly == null) { - mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - } - return mConnManagerDoNotUseDirectly; - } - } - - /** - * Should only be created after {@link ContentService#systemReady()} so that - * {@link PackageManager} is ready to query. - */ - public SyncManager(Context context, boolean factoryTest) { - // Initialize the SyncStorageEngine first, before registering observers - // and creating threads and so on; it may fail if the disk is full. - mContext = context; - - SyncStorageEngine.init(context); - mSyncStorageEngine = SyncStorageEngine.getSingleton(); - mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { - public void onSyncRequest(Account account, int userId, String authority, - Bundle extras) { - scheduleSync(account, userId, authority, extras, 0, false); - } - }); - - mSyncAdapters = new SyncAdaptersCache(mContext); - mSyncQueue = new SyncQueue(mSyncStorageEngine, mSyncAdapters); - - HandlerThread syncThread = new HandlerThread("SyncHandlerThread", - Process.THREAD_PRIORITY_BACKGROUND); - syncThread.start(); - mSyncHandler = new SyncHandler(syncThread.getLooper()); - - mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { - @Override - public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) { - if (!removed) { - scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */, - false /* onlyThoseWithUnkownSyncableState */); - } - } - }, mSyncHandler); - - mSyncAlarmIntent = PendingIntent.getBroadcast( - mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0); - - IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); - context.registerReceiver(mConnectivityIntentReceiver, intentFilter); - - if (!factoryTest) { - intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - context.registerReceiver(mBootCompletedReceiver, intentFilter); - } - - intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); - context.registerReceiver(mBackgroundDataSettingChanged, intentFilter); - - intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); - intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); - context.registerReceiver(mStorageIntentReceiver, intentFilter); - - intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); - intentFilter.setPriority(100); - context.registerReceiver(mShutdownIntentReceiver, intentFilter); - - intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_REMOVED); - intentFilter.addAction(Intent.ACTION_USER_STARTING); - intentFilter.addAction(Intent.ACTION_USER_STOPPING); - mContext.registerReceiverAsUser( - mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); - - if (!factoryTest) { - mNotificationMgr = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - context.registerReceiver(new SyncAlarmIntentReceiver(), - new IntentFilter(ACTION_SYNC_ALARM)); - } else { - mNotificationMgr = null; - } - mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - - // This WakeLock is used to ensure that we stay awake between the time that we receive - // a sync alarm notification and when we finish processing it. We need to do this - // because we don't do the work in the alarm handler, rather we do it in a message - // handler. - mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - HANDLE_SYNC_ALARM_WAKE_LOCK); - mHandleAlarmWakeLock.setReferenceCounted(false); - - // This WakeLock is used to ensure that we stay awake while running the sync loop - // message handler. Normally we will hold a sync adapter wake lock while it is being - // synced but during the execution of the sync loop it might finish a sync for - // one sync adapter before starting the sync for the other sync adapter and we - // don't want the device to go to sleep during that window. - mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - SYNC_LOOP_WAKE_LOCK); - mSyncManagerWakeLock.setReferenceCounted(false); - - mSyncStorageEngine.addStatusChangeListener( - ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() { - public void onStatusChanged(int which) { - // force the sync loop to run if the settings change - sendCheckAlarmsMessage(); - } - }); - - if (!factoryTest) { - // Register for account list updates for all users - mContext.registerReceiverAsUser(mAccountsUpdatedReceiver, - UserHandle.ALL, - new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), - null, null); - } - - // Pick a random second in a day to seed all periodic syncs - mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000; - } - - /** - * Return a random value v that satisfies minValue <= v < maxValue. The difference between - * maxValue and minValue must be less than Integer.MAX_VALUE. - */ - private long jitterize(long minValue, long maxValue) { - Random random = new Random(SystemClock.elapsedRealtime()); - long spread = maxValue - minValue; - if (spread > Integer.MAX_VALUE) { - throw new IllegalArgumentException("the difference between the maxValue and the " - + "minValue must be less than " + Integer.MAX_VALUE); - } - return minValue + random.nextInt((int)spread); - } - - public SyncStorageEngine getSyncStorageEngine() { - return mSyncStorageEngine; - } - - private void ensureAlarmService() { - if (mAlarmService == null) { - mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); - } - } - - /** - * Initiate a sync. This can start a sync for all providers - * (pass null to url, set onlyTicklable to false), only those - * providers that are marked as ticklable (pass null to url, - * set onlyTicklable to true), or a specific provider (set url - * to the content url of the provider). - * - * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is - * true then initiate a sync that just checks for local changes to send - * to the server, otherwise initiate a sync that first gets any - * changes from the server before sending local changes back to - * the server. - * - * <p>If a specific provider is being synced (the url is non-null) - * then the extras can contain SyncAdapter-specific information - * to control what gets synced (e.g. which specific feed to sync). - * - * <p>You'll start getting callbacks after this. - * - * @param requestedAccount the account to sync, may be null to signify all accounts - * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL, - * then all users' accounts are considered. - * @param requestedAuthority the authority to sync, may be null to indicate all authorities - * @param extras a Map of SyncAdapter-specific information to control - * syncs of a specific provider. Can be null. Is ignored - * if the url is null. - * @param delay how many milliseconds in the future to wait before performing this - * @param onlyThoseWithUnkownSyncableState - */ - public void scheduleSync(Account requestedAccount, int userId, String requestedAuthority, - Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - - final boolean backgroundDataUsageAllowed = !mBootCompleted || - getConnectivityManager().getBackgroundDataSetting(); - - if (extras == null) extras = new Bundle(); - - Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); - if (expedited) { - delay = -1; // this means schedule at the front of the queue - } - - AccountAndUser[] accounts; - if (requestedAccount != null && userId != UserHandle.USER_ALL) { - accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; - } else { - // if the accounts aren't configured yet then we can't support an account-less - // sync request - accounts = mRunningAccounts; - if (accounts.length == 0) { - if (isLoggable) { - Log.v(TAG, "scheduleSync: no accounts configured, dropping"); - } - return; - } - } - - final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); - final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - if (manualSync) { - extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); - } - final boolean ignoreSettings = - extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); - - int source; - if (uploadOnly) { - source = SyncStorageEngine.SOURCE_LOCAL; - } else if (manualSync) { - source = SyncStorageEngine.SOURCE_USER; - } else if (requestedAuthority == null) { - source = SyncStorageEngine.SOURCE_POLL; - } else { - // this isn't strictly server, since arbitrary callers can (and do) request - // a non-forced two-way sync on a specific url - source = SyncStorageEngine.SOURCE_SERVER; - } - - for (AccountAndUser account : accounts) { - // Compile a list of authorities that have sync adapters. - // For each authority sync each account that matches a sync adapter. - final HashSet<String> syncableAuthorities = new HashSet<String>(); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : - mSyncAdapters.getAllServices(account.userId)) { - syncableAuthorities.add(syncAdapter.type.authority); - } - - // if the url was specified then replace the list of authorities - // with just this authority or clear it if this authority isn't - // syncable - if (requestedAuthority != null) { - final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); - syncableAuthorities.clear(); - if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); - } - - for (String authority : syncableAuthorities) { - int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, - authority); - if (isSyncable == 0) { - continue; - } - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.account.type), account.userId); - if (syncAdapterInfo == null) { - continue; - } - final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); - final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); - if (isSyncable < 0 && isAlwaysSyncable) { - mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1); - isSyncable = 1; - } - if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { - continue; - } - if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { - continue; - } - - // always allow if the isSyncable state is unknown - boolean syncAllowed = - (isSyncable < 0) - || ignoreSettings - || (backgroundDataUsageAllowed - && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) - && mSyncStorageEngine.getSyncAutomatically(account.account, - account.userId, authority)); - if (!syncAllowed) { - if (isLoggable) { - Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority - + " is not allowed, dropping request"); - } - continue; - } - - Pair<Long, Long> backoff = mSyncStorageEngine - .getBackoff(account.account, account.userId, authority); - long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account, - account.userId, authority); - final long backoffTime = backoff != null ? backoff.first : 0; - if (isSyncable < 0) { - Bundle newExtras = new Bundle(); - newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); - if (isLoggable) { - Log.v(TAG, "scheduleSync:" - + " delay " + delay - + ", source " + source - + ", account " + account - + ", authority " + authority - + ", extras " + newExtras); - } - scheduleSyncOperation( - new SyncOperation(account.account, account.userId, source, authority, - newExtras, 0, backoffTime, delayUntil, allowParallelSyncs)); - } - if (!onlyThoseWithUnkownSyncableState) { - if (isLoggable) { - Log.v(TAG, "scheduleSync:" - + " delay " + delay - + ", source " + source - + ", account " + account - + ", authority " + authority - + ", extras " + extras); - } - scheduleSyncOperation( - new SyncOperation(account.account, account.userId, source, authority, - extras, delay, backoffTime, delayUntil, allowParallelSyncs)); - } - } - } - } - - public void scheduleLocalSync(Account account, int userId, String authority) { - final Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); - scheduleSync(account, userId, authority, extras, LOCAL_SYNC_DELAY, - false /* onlyThoseWithUnkownSyncableState */); - } - - public SyncAdapterType[] getSyncAdapterTypes(int userId) { - final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; - serviceInfos = mSyncAdapters.getAllServices(userId); - SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; - int i = 0; - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { - types[i] = serviceInfo.type; - ++i; - } - return types; - } - - private void sendSyncAlarmMessage() { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM"); - mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM); - } - - private void sendCheckAlarmsMessage() { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS"); - mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS); - mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS); - } - - private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, - SyncResult syncResult) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED"); - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_SYNC_FINISHED; - msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult); - mSyncHandler.sendMessage(msg); - } - - private void sendCancelSyncsMessage(final Account account, final int userId, - final String authority) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL"); - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_CANCEL; - msg.obj = Pair.create(account, authority); - msg.arg1 = userId; - mSyncHandler.sendMessage(msg); - } - - class SyncHandlerMessagePayload { - public final ActiveSyncContext activeSyncContext; - public final SyncResult syncResult; - - SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) { - this.activeSyncContext = syncContext; - this.syncResult = syncResult; - } - } - - class SyncAlarmIntentReceiver extends BroadcastReceiver { - public void onReceive(Context context, Intent intent) { - mHandleAlarmWakeLock.acquire(); - sendSyncAlarmMessage(); - } - } - - private void clearBackoffSetting(SyncOperation op) { - mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, - SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); - synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0); - } - } - - private void increaseBackoffSetting(SyncOperation op) { - // TODO: Use this function to align it to an already scheduled sync - // operation in the specified window - final long now = SystemClock.elapsedRealtime(); - - final Pair<Long, Long> previousSettings = - mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority); - long newDelayInMs = -1; - if (previousSettings != null) { - // don't increase backoff before current backoff is expired. This will happen for op's - // with ignoreBackoff set. - if (now < previousSettings.first) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Still in backoff, do not increase it. " - + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds."); - } - return; - } - // Subsequent delays are the double of the previous delay - newDelayInMs = previousSettings.second * 2; - } - if (newDelayInMs <= 0) { - // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS - newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS, - (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1)); - } - - // Cap the delay - long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(), - Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS, - DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS); - if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { - newDelayInMs = maxSyncRetryTimeInSeconds * 1000; - } - - final long backoff = now + newDelayInMs; - - mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, - backoff, newDelayInMs); - - op.backoff = backoff; - op.updateEffectiveRunTime(); - - synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff); - } - } - - private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) { - final long delayUntil = delayUntilSeconds * 1000; - final long absoluteNow = System.currentTimeMillis(); - long newDelayUntilTime; - if (delayUntil > absoluteNow) { - newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow); - } else { - newDelayUntilTime = 0; - } - mSyncStorageEngine - .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime); - synchronized (mSyncQueue) { - mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime); - } - } - - /** - * Cancel the active sync if it matches the authority and account. - * @param account limit the cancelations to syncs with this account, if non-null - * @param authority limit the cancelations to syncs with this authority, if non-null - */ - public void cancelActiveSync(Account account, int userId, String authority) { - sendCancelSyncsMessage(account, userId, authority); - } - - /** - * Create and schedule a SyncOperation. - * - * @param syncOperation the SyncOperation to schedule - */ - public void scheduleSyncOperation(SyncOperation syncOperation) { - boolean queueChanged; - synchronized (mSyncQueue) { - queueChanged = mSyncQueue.add(syncOperation); - } - - if (queueChanged) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation); - } - sendCheckAlarmsMessage(); - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation " - + syncOperation); - } - } - } - - /** - * Remove scheduled sync operations. - * @param account limit the removals to operations with this account, if non-null - * @param authority limit the removals to operations with this authority, if non-null - */ - public void clearScheduledSyncOperations(Account account, int userId, String authority) { - synchronized (mSyncQueue) { - mSyncQueue.remove(account, userId, authority); - } - mSyncStorageEngine.setBackoff(account, userId, authority, - SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); - } - - void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) { - boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); - if (isLoggable) { - Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation); - } - - operation = new SyncOperation(operation); - - // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given - // request. Retries of the request will always honor the backoff, so clear the - // flag in case we retry this request. - if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) { - operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); - } - - // If this sync aborted because the internal sync loop retried too many times then - // don't reschedule. Otherwise we risk getting into a retry loop. - // If the operation succeeded to some extent then retry immediately. - // If this was a two-way sync then retry soft errors with an exponential backoff. - // If this was an upward sync then schedule a two-way sync immediately. - // Otherwise do not reschedule. - if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) { - Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " - + operation); - } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) - && !syncResult.syncAlreadyInProgress) { - operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); - Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " - + "encountered an error: " + operation); - scheduleSyncOperation(operation); - } else if (syncResult.tooManyRetries) { - Log.d(TAG, "not retrying sync operation because it retried too many times: " - + operation); - } else if (syncResult.madeSomeProgress()) { - if (isLoggable) { - Log.d(TAG, "retrying sync operation because even though it had an error " - + "it achieved some success"); - } - scheduleSyncOperation(operation); - } else if (syncResult.syncAlreadyInProgress) { - if (isLoggable) { - Log.d(TAG, "retrying sync operation that failed because there was already a " - + "sync in progress: " + operation); - } - scheduleSyncOperation(new SyncOperation(operation.account, operation.userId, - operation.syncSource, - operation.authority, operation.extras, - DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, - operation.backoff, operation.delayUntil, operation.allowParallelSyncs)); - } else if (syncResult.hasSoftError()) { - if (isLoggable) { - Log.d(TAG, "retrying sync operation because it encountered a soft error: " - + operation); - } - scheduleSyncOperation(operation); - } else { - Log.d(TAG, "not retrying sync operation because the error is a hard error: " - + operation); - } - } - - private void onUserStarting(int userId) { - // Make sure that accounts we're about to use are valid - AccountManagerService.getSingleton().validateAccounts(userId); - - mSyncAdapters.invalidateCache(userId); - - updateRunningAccounts(); - - synchronized (mSyncQueue) { - mSyncQueue.addPendingOperations(userId); - } - - // Schedule sync for any accounts under started user - final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId); - for (Account account : accounts) { - scheduleSync(account, userId, null, null, 0 /* no delay */, - true /* onlyThoseWithUnknownSyncableState */); - } - - sendCheckAlarmsMessage(); - } - - private void onUserStopping(int userId) { - updateRunningAccounts(); - - cancelActiveSync( - null /* any account */, - userId, - null /* any authority */); - } - - private void onUserRemoved(int userId) { - updateRunningAccounts(); - - // Clean up the storage engine database - mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); - synchronized (mSyncQueue) { - mSyncQueue.removeUser(userId); - } - } - - /** - * @hide - */ - class ActiveSyncContext extends ISyncContext.Stub - implements ServiceConnection, IBinder.DeathRecipient { - final SyncOperation mSyncOperation; - final long mHistoryRowId; - ISyncAdapter mSyncAdapter; - final long mStartTime; - long mTimeoutStartTime; - boolean mBound; - final PowerManager.WakeLock mSyncWakeLock; - final int mSyncAdapterUid; - SyncInfo mSyncInfo; - boolean mIsLinkedToDeath = false; - - /** - * Create an ActiveSyncContext for an impending sync and grab the wakelock for that - * sync adapter. Since this grabs the wakelock you need to be sure to call - * close() when you are done with this ActiveSyncContext, whether the sync succeeded - * or not. - * @param syncOperation the SyncOperation we are about to sync - * @param historyRowId the row in which to record the history info for this sync - * @param syncAdapterUid the UID of the application that contains the sync adapter - * for this sync. This is used to attribute the wakelock hold to that application. - */ - public ActiveSyncContext(SyncOperation syncOperation, long historyRowId, - int syncAdapterUid) { - super(); - mSyncAdapterUid = syncAdapterUid; - mSyncOperation = syncOperation; - mHistoryRowId = historyRowId; - mSyncAdapter = null; - mStartTime = SystemClock.elapsedRealtime(); - mTimeoutStartTime = mStartTime; - mSyncWakeLock = mSyncHandler.getSyncWakeLock( - mSyncOperation.account, mSyncOperation.authority); - mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); - mSyncWakeLock.acquire(); - } - - public void sendHeartbeat() { - // heartbeats are no longer used - } - - public void onFinished(SyncResult result) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this); - // include "this" in the message so that the handler can ignore it if this - // ActiveSyncContext is no longer the mActiveSyncContext at message handling - // time - sendSyncFinishedOrCanceledMessage(this, result); - } - - public void toString(StringBuilder sb) { - sb.append("startTime ").append(mStartTime) - .append(", mTimeoutStartTime ").append(mTimeoutStartTime) - .append(", mHistoryRowId ").append(mHistoryRowId) - .append(", syncOperation ").append(mSyncOperation); - } - - public void onServiceConnected(ComponentName name, IBinder service) { - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; - msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service)); - mSyncHandler.sendMessage(msg); - } - - public void onServiceDisconnected(ComponentName name) { - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED; - msg.obj = new ServiceConnectionData(this, null); - mSyncHandler.sendMessage(msg); - } - - boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this); - } - Intent intent = new Intent(); - intent.setAction("android.content.SyncAdapter"); - intent.setComponent(info.componentName); - intent.putExtra(Intent.EXTRA_CLIENT_LABEL, - com.android.internal.R.string.sync_binding_label); - intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( - mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0, - null, new UserHandle(userId))); - mBound = true; - final boolean bindResult = mContext.bindService(intent, this, - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT, - mSyncOperation.userId); - if (!bindResult) { - mBound = false; - } - return bindResult; - } - - /** - * Performs the required cleanup, which is the releasing of the wakelock and - * unbinding from the sync adapter (if actually bound). - */ - protected void close() { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "unBindFromSyncAdapter: connection " + this); - } - if (mBound) { - mBound = false; - mContext.unbindService(this); - } - mSyncWakeLock.release(); - mSyncWakeLock.setWorkSource(null); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - @Override - public void binderDied() { - sendSyncFinishedOrCanceledMessage(this, null); - } - } - - protected void dump(FileDescriptor fd, PrintWriter pw) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - dumpSyncState(ipw); - dumpSyncHistory(ipw); - dumpSyncAdapters(ipw); - } - - static String formatTime(long time) { - Time tobj = new Time(); - tobj.set(time); - return tobj.format("%Y-%m-%d %H:%M:%S"); - } - - protected void dumpSyncState(PrintWriter pw) { - pw.print("data connected: "); pw.println(mDataConnectionIsConnected); - pw.print("auto sync: "); - List<UserInfo> users = getAllUsers(); - if (users != null) { - for (UserInfo user : users) { - pw.print("u" + user.id + "=" - + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); - } - pw.println(); - } - pw.print("memory low: "); pw.println(mStorageIsLow); - - final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); - - pw.print("accounts: "); - if (accounts != INITIAL_ACCOUNTS_ARRAY) { - pw.println(accounts.length); - } else { - pw.println("not known yet"); - } - final long now = SystemClock.elapsedRealtime(); - pw.print("now: "); pw.print(now); - pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); - pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000)); - pw.println(" (HH:MM:SS)"); - pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000)); - pw.println(" (HH:MM:SS)"); - pw.print("time spent syncing: "); - pw.print(DateUtils.formatElapsedTime( - mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000)); - pw.print(" (HH:MM:SS), sync "); - pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not "); - pw.println("in progress"); - if (mSyncHandler.mAlarmScheduleTime != null) { - pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime); - pw.print(" ("); - pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000)); - pw.println(" (HH:MM:SS) from now)"); - } else { - pw.println("no alarm is scheduled (there had better not be any pending syncs)"); - } - - pw.print("notification info: "); - final StringBuilder sb = new StringBuilder(); - mSyncHandler.mSyncNotificationInfo.toString(sb); - pw.println(sb.toString()); - - pw.println(); - pw.println("Active Syncs: " + mActiveSyncContexts.size()); - for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000; - pw.print(" "); - pw.print(DateUtils.formatElapsedTime(durationInSeconds)); - pw.print(" - "); - pw.print(activeSyncContext.mSyncOperation.dump(false)); - pw.println(); - } - - synchronized (mSyncQueue) { - sb.setLength(0); - mSyncQueue.dump(sb); - } - pw.println(); - pw.print(sb.toString()); - - // join the installed sync adapter with the accounts list and emit for everything - pw.println(); - pw.println("Sync Status"); - for (AccountAndUser account : accounts) { - pw.print(" Account "); pw.print(account.account.name); - pw.print(" u"); pw.print(account.userId); - pw.print(" "); pw.print(account.account.type); - pw.println(":"); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : - mSyncAdapters.getAllServices(account.userId)) { - if (!syncAdapterType.type.accountType.equals(account.account.type)) { - continue; - } - - SyncStorageEngine.AuthorityInfo settings = - mSyncStorageEngine.getOrCreateAuthority( - account.account, account.userId, syncAdapterType.type.authority); - SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings); - pw.print(" "); pw.print(settings.authority); - pw.println(":"); - pw.print(" settings:"); - pw.print(" " + (settings.syncable > 0 - ? "syncable" - : (settings.syncable == 0 ? "not syncable" : "not initialized"))); - pw.print(", " + (settings.enabled ? "enabled" : "disabled")); - if (settings.delayUntil > now) { - pw.print(", delay for " - + ((settings.delayUntil - now) / 1000) + " sec"); - } - if (settings.backoffTime > now) { - pw.print(", backoff for " - + ((settings.backoffTime - now) / 1000) + " sec"); - } - if (settings.backoffDelay > 0) { - pw.print(", the backoff increment is " + settings.backoffDelay / 1000 - + " sec"); - } - pw.println(); - for (int periodicIndex = 0; - periodicIndex < settings.periodicSyncs.size(); - periodicIndex++) { - Pair<Bundle, Long> info = settings.periodicSyncs.get(periodicIndex); - long lastPeriodicTime = status.getPeriodicSyncTime(periodicIndex); - long nextPeriodicTime = lastPeriodicTime + info.second * 1000; - pw.println(" periodic period=" + info.second - + ", extras=" + info.first - + ", next=" + formatTime(nextPeriodicTime)); - } - pw.print(" count: local="); pw.print(status.numSourceLocal); - pw.print(" poll="); pw.print(status.numSourcePoll); - pw.print(" periodic="); pw.print(status.numSourcePeriodic); - pw.print(" server="); pw.print(status.numSourceServer); - pw.print(" user="); pw.print(status.numSourceUser); - pw.print(" total="); pw.print(status.numSyncs); - pw.println(); - pw.print(" total duration: "); - pw.println(DateUtils.formatElapsedTime(status.totalElapsedTime/1000)); - if (status.lastSuccessTime != 0) { - pw.print(" SUCCESS: source="); - pw.print(SyncStorageEngine.SOURCES[status.lastSuccessSource]); - pw.print(" time="); - pw.println(formatTime(status.lastSuccessTime)); - } - if (status.lastFailureTime != 0) { - pw.print(" FAILURE: source="); - pw.print(SyncStorageEngine.SOURCES[ - status.lastFailureSource]); - pw.print(" initialTime="); - pw.print(formatTime(status.initialFailureTime)); - pw.print(" lastTime="); - pw.println(formatTime(status.lastFailureTime)); - int errCode = status.getLastFailureMesgAsInt(0); - pw.print(" message: "); pw.println( - getLastFailureMessage(errCode) + " (" + errCode + ")"); - } - } - } - } - - private String getLastFailureMessage(int code) { - switch (code) { - case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS: - return "sync already in progress"; - - case ContentResolver.SYNC_ERROR_AUTHENTICATION: - return "authentication error"; - - case ContentResolver.SYNC_ERROR_IO: - return "I/O error"; - - case ContentResolver.SYNC_ERROR_PARSE: - return "parse error"; - - case ContentResolver.SYNC_ERROR_CONFLICT: - return "conflict error"; - - case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS: - return "too many deletions error"; - - case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES: - return "too many retries error"; - - case ContentResolver.SYNC_ERROR_INTERNAL: - return "internal error"; - - default: - return "unknown"; - } - } - - private void dumpTimeSec(PrintWriter pw, long time) { - pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); - pw.print('s'); - } - - private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { - pw.print("Success ("); pw.print(ds.successCount); - if (ds.successCount > 0) { - pw.print(" for "); dumpTimeSec(pw, ds.successTime); - pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); - } - pw.print(") Failure ("); pw.print(ds.failureCount); - if (ds.failureCount > 0) { - pw.print(" for "); dumpTimeSec(pw, ds.failureTime); - pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); - } - pw.println(")"); - } - - protected void dumpSyncHistory(PrintWriter pw) { - dumpRecentHistory(pw); - dumpDayStatistics(pw); - } - - private void dumpRecentHistory(PrintWriter pw) { - final ArrayList<SyncStorageEngine.SyncHistoryItem> items - = mSyncStorageEngine.getSyncHistory(); - if (items != null && items.size() > 0) { - final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); - long totalElapsedTime = 0; - long totalTimes = 0; - final int N = items.size(); - - int maxAuthority = 0; - int maxAccount = 0; - for (SyncStorageEngine.SyncHistoryItem item : items) { - SyncStorageEngine.AuthorityInfo authority - = mSyncStorageEngine.getAuthority(item.authorityId); - final String authorityName; - final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; - } else { - authorityName = "Unknown"; - accountKey = "Unknown"; - } - - int length = authorityName.length(); - if (length > maxAuthority) { - maxAuthority = length; - } - length = accountKey.length(); - if (length > maxAccount) { - maxAccount = length; - } - - final long elapsedTime = item.elapsedTime; - totalElapsedTime += elapsedTime; - totalTimes++; - AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); - if (authoritySyncStats == null) { - authoritySyncStats = new AuthoritySyncStats(authorityName); - authorityMap.put(authorityName, authoritySyncStats); - } - authoritySyncStats.elapsedTime += elapsedTime; - authoritySyncStats.times++; - final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; - AccountSyncStats accountSyncStats = accountMap.get(accountKey); - if (accountSyncStats == null) { - accountSyncStats = new AccountSyncStats(accountKey); - accountMap.put(accountKey, accountSyncStats); - } - accountSyncStats.elapsedTime += elapsedTime; - accountSyncStats.times++; - - } - - if (totalElapsedTime > 0) { - pw.println(); - pw.printf("Detailed Statistics (Recent history): " - + "%d (# of times) %ds (sync time)\n", - totalTimes, totalElapsedTime / 1000); - - final List<AuthoritySyncStats> sortedAuthorities = - new ArrayList<AuthoritySyncStats>(authorityMap.values()); - Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { - @Override - public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { - // reverse order - int compare = Integer.compare(rhs.times, lhs.times); - if (compare == 0) { - compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); - } - return compare; - } - }); - - final int maxLength = Math.max(maxAuthority, maxAccount + 3); - final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; - final char chars[] = new char[padLength]; - Arrays.fill(chars, '-'); - final String separator = new String(chars); - - final String authorityFormat = - String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); - final String accountFormat = - String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); - - pw.println(separator); - for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { - String name = authoritySyncStats.name; - long elapsedTime; - int times; - String timeStr; - String timesStr; - - elapsedTime = authoritySyncStats.elapsedTime; - times = authoritySyncStats.times; - timeStr = String.format("%ds/%d%%", - elapsedTime / 1000, - elapsedTime * 100 / totalElapsedTime); - timesStr = String.format("%d/%d%%", - times, - times * 100 / totalTimes); - pw.printf(authorityFormat, name, timesStr, timeStr); - - final List<AccountSyncStats> sortedAccounts = - new ArrayList<AccountSyncStats>( - authoritySyncStats.accountMap.values()); - Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { - @Override - public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { - // reverse order - int compare = Integer.compare(rhs.times, lhs.times); - if (compare == 0) { - compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); - } - return compare; - } - }); - for (AccountSyncStats stats: sortedAccounts) { - elapsedTime = stats.elapsedTime; - times = stats.times; - timeStr = String.format("%ds/%d%%", - elapsedTime / 1000, - elapsedTime * 100 / totalElapsedTime); - timesStr = String.format("%d/%d%%", - times, - times * 100 / totalTimes); - pw.printf(accountFormat, stats.name, timesStr, timeStr); - } - pw.println(separator); - } - } - - pw.println(); - pw.println("Recent Sync History"); - final String format = " %-" + maxAccount + "s %s\n"; - final Map<String, Long> lastTimeMap = Maps.newHashMap(); - - for (int i = 0; i < N; i++) { - SyncStorageEngine.SyncHistoryItem item = items.get(i); - SyncStorageEngine.AuthorityInfo authority - = mSyncStorageEngine.getAuthority(item.authorityId); - final String authorityName; - final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; - } else { - authorityName = "Unknown"; - accountKey = "Unknown"; - } - final long elapsedTime = item.elapsedTime; - final Time time = new Time(); - final long eventTime = item.eventTime; - time.set(eventTime); - - final String key = authorityName + "/" + accountKey; - final Long lastEventTime = lastTimeMap.get(key); - final String diffString; - if (lastEventTime == null) { - diffString = ""; - } else { - final long diff = (lastEventTime - eventTime) / 1000; - if (diff < 60) { - diffString = String.valueOf(diff); - } else if (diff < 3600) { - diffString = String.format("%02d:%02d", diff / 60, diff % 60); - } else { - final long sec = diff % 3600; - diffString = String.format("%02d:%02d:%02d", - diff / 3600, sec / 60, sec % 60); - } - } - lastTimeMap.put(key, eventTime); - - pw.printf(" #%-3d: %s %8s %5.1fs %8s", - i + 1, - formatTime(eventTime), - SyncStorageEngine.SOURCES[item.source], - ((float) elapsedTime) / 1000, - diffString); - pw.printf(format, accountKey, authorityName); - - if (item.event != SyncStorageEngine.EVENT_STOP - || item.upstreamActivity != 0 - || item.downstreamActivity != 0) { - pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", - item.event, - item.upstreamActivity, - item.downstreamActivity); - } - if (item.mesg != null - && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { - pw.printf(" mesg=%s\n", item.mesg); - } - } - } - } - - private void dumpDayStatistics(PrintWriter pw) { - SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); - if (dses != null && dses[0] != null) { - pw.println(); - pw.println("Sync Statistics"); - pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); - int today = dses[0].day; - int i; - SyncStorageEngine.DayStats ds; - - // Print each day in the current week. - for (i=1; i<=6 && i < dses.length; i++) { - ds = dses[i]; - if (ds == null) break; - int delta = today-ds.day; - if (delta > 6) break; - - pw.print(" Day-"); pw.print(delta); pw.print(": "); - dumpDayStatistic(pw, ds); - } - - // Aggregate all following days into weeks and print totals. - int weekDay = today; - while (i < dses.length) { - SyncStorageEngine.DayStats aggr = null; - weekDay -= 7; - while (i < dses.length) { - ds = dses[i]; - if (ds == null) { - i = dses.length; - break; - } - int delta = weekDay-ds.day; - if (delta > 6) break; - i++; - - if (aggr == null) { - aggr = new SyncStorageEngine.DayStats(weekDay); - } - aggr.successCount += ds.successCount; - aggr.successTime += ds.successTime; - aggr.failureCount += ds.failureCount; - aggr.failureTime += ds.failureTime; - } - if (aggr != null) { - pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); - dumpDayStatistic(pw, aggr); - } - } - } - } - - private void dumpSyncAdapters(IndentingPrintWriter pw) { - pw.println(); - final List<UserInfo> users = getAllUsers(); - if (users != null) { - for (UserInfo user : users) { - pw.println("Sync adapters for " + user + ":"); - pw.increaseIndent(); - for (RegisteredServicesCache.ServiceInfo<?> info : - mSyncAdapters.getAllServices(user.id)) { - pw.println(info); - } - pw.decreaseIndent(); - pw.println(); - } - } - } - - private static class AuthoritySyncStats { - String name; - long elapsedTime; - int times; - Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); - - private AuthoritySyncStats(String name) { - this.name = name; - } - } - - private static class AccountSyncStats { - String name; - long elapsedTime; - int times; - - private AccountSyncStats(String name) { - this.name = name; - } - } - - /** - * A helper object to keep track of the time we have spent syncing since the last boot - */ - private class SyncTimeTracker { - /** True if a sync was in progress on the most recent call to update() */ - boolean mLastWasSyncing = false; - /** Used to track when lastWasSyncing was last set */ - long mWhenSyncStarted = 0; - /** The cumulative time we have spent syncing */ - private long mTimeSpentSyncing; - - /** Call to let the tracker know that the sync state may have changed */ - public synchronized void update() { - final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); - if (isSyncInProgress == mLastWasSyncing) return; - final long now = SystemClock.elapsedRealtime(); - if (isSyncInProgress) { - mWhenSyncStarted = now; - } else { - mTimeSpentSyncing += now - mWhenSyncStarted; - } - mLastWasSyncing = isSyncInProgress; - } - - /** Get how long we have been syncing, in ms */ - public synchronized long timeSpentSyncing() { - if (!mLastWasSyncing) return mTimeSpentSyncing; - - final long now = SystemClock.elapsedRealtime(); - return mTimeSpentSyncing + (now - mWhenSyncStarted); - } - } - - class ServiceConnectionData { - public final ActiveSyncContext activeSyncContext; - public final ISyncAdapter syncAdapter; - ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) { - this.activeSyncContext = activeSyncContext; - this.syncAdapter = syncAdapter; - } - } - - /** - * Handles SyncOperation Messages that are posted to the associated - * HandlerThread. - */ - class SyncHandler extends Handler { - // Messages that can be sent on mHandler - private static final int MESSAGE_SYNC_FINISHED = 1; - private static final int MESSAGE_SYNC_ALARM = 2; - private static final int MESSAGE_CHECK_ALARMS = 3; - private static final int MESSAGE_SERVICE_CONNECTED = 4; - private static final int MESSAGE_SERVICE_DISCONNECTED = 5; - private static final int MESSAGE_CANCEL = 6; - - public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); - private Long mAlarmScheduleTime = null; - public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); - private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks = - Maps.newHashMap(); - - private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); - - public void onBootCompleted() { - mBootCompleted = true; - - doDatabaseCleanup(); - - if (mReadyToRunLatch != null) { - mReadyToRunLatch.countDown(); - } - } - - private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) { - final Pair<Account, String> wakeLockKey = Pair.create(account, authority); - PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); - if (wakeLock == null) { - final String name = SYNC_WAKE_LOCK_PREFIX + "_" + authority + "_" + account; - wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); - wakeLock.setReferenceCounted(false); - mWakeLocks.put(wakeLockKey, wakeLock); - } - return wakeLock; - } - - private void waitUntilReadyToRun() { - CountDownLatch latch = mReadyToRunLatch; - if (latch != null) { - while (true) { - try { - latch.await(); - mReadyToRunLatch = null; - return; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - } - /** - * Used to keep track of whether a sync notification is active and who it is for. - */ - class SyncNotificationInfo { - // true iff the notification manager has been asked to send the notification - public boolean isActive = false; - - // Set when we transition from not running a sync to running a sync, and cleared on - // the opposite transition. - public Long startTime = null; - - public void toString(StringBuilder sb) { - sb.append("isActive ").append(isActive).append(", startTime ").append(startTime); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - } - - public SyncHandler(Looper looper) { - super(looper); - } - - public void handleMessage(Message msg) { - long earliestFuturePollTime = Long.MAX_VALUE; - long nextPendingSyncTime = Long.MAX_VALUE; - - // Setting the value here instead of a method because we want the dumpsys logs - // to have the most recent value used. - try { - waitUntilReadyToRun(); - mDataConnectionIsConnected = readDataConnectionState(); - mSyncManagerWakeLock.acquire(); - // Always do this first so that we be sure that any periodic syncs that - // are ready to run have been converted into pending syncs. This allows the - // logic that considers the next steps to take based on the set of pending syncs - // to also take into account the periodic syncs. - earliestFuturePollTime = scheduleReadyPeriodicSyncs(); - switch (msg.what) { - case SyncHandler.MESSAGE_CANCEL: { - Pair<Account, String> payload = (Pair<Account, String>)msg.obj; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " - + payload.first + ", " + payload.second); - } - cancelActiveSyncLocked(payload.first, msg.arg1, payload.second); - nextPendingSyncTime = maybeStartNextSyncLocked(); - break; - } - - case SyncHandler.MESSAGE_SYNC_FINISHED: - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED"); - } - SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj; - if (!isSyncStillActive(payload.activeSyncContext)) { - Log.d(TAG, "handleSyncHandlerMessage: dropping since the " - + "sync is no longer active: " - + payload.activeSyncContext); - break; - } - runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext); - - // since a sync just finished check if it is time to start a new sync - nextPendingSyncTime = maybeStartNextSyncLocked(); - break; - - case SyncHandler.MESSAGE_SERVICE_CONNECTED: { - ServiceConnectionData msgData = (ServiceConnectionData)msg.obj; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " - + msgData.activeSyncContext); - } - // check that this isn't an old message - if (isSyncStillActive(msgData.activeSyncContext)) { - runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter); - } - break; - } - - case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { - final ActiveSyncContext currentSyncContext = - ((ServiceConnectionData)msg.obj).activeSyncContext; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " - + currentSyncContext); - } - // check that this isn't an old message - if (isSyncStillActive(currentSyncContext)) { - // cancel the sync if we have a syncadapter, which means one is - // outstanding - if (currentSyncContext.mSyncAdapter != null) { - try { - currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); - } catch (RemoteException e) { - // we don't need to retry this in this case - } - } - - // pretend that the sync failed with an IOException, - // which is a soft error - SyncResult syncResult = new SyncResult(); - syncResult.stats.numIoExceptions++; - runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext); - - // since a sync just finished check if it is time to start a new sync - nextPendingSyncTime = maybeStartNextSyncLocked(); - } - - break; - } - - case SyncHandler.MESSAGE_SYNC_ALARM: { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - if (isLoggable) { - Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM"); - } - mAlarmScheduleTime = null; - try { - nextPendingSyncTime = maybeStartNextSyncLocked(); - } finally { - mHandleAlarmWakeLock.release(); - } - break; - } - - case SyncHandler.MESSAGE_CHECK_ALARMS: - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS"); - } - nextPendingSyncTime = maybeStartNextSyncLocked(); - break; - } - } finally { - manageSyncNotificationLocked(); - manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime); - mSyncTimeTracker.update(); - mSyncManagerWakeLock.release(); - } - } - - /** - * Turn any periodic sync operations that are ready to run into pending sync operations. - * @return the desired start time of the earliest future periodic sync operation, - * in milliseconds since boot - */ - private long scheduleReadyPeriodicSyncs() { - final boolean backgroundDataUsageAllowed = - getConnectivityManager().getBackgroundDataSetting(); - long earliestFuturePollTime = Long.MAX_VALUE; - if (!backgroundDataUsageAllowed) { - return earliestFuturePollTime; - } - - AccountAndUser[] accounts = mRunningAccounts; - - final long nowAbsolute = System.currentTimeMillis(); - final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) - ? (nowAbsolute - mSyncRandomOffsetMillis) : 0; - - ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities(); - for (SyncStorageEngine.AuthorityInfo info : infos) { - // skip the sync if the account of this operation no longer exists - if (!containsAccountAndUser(accounts, info.account, info.userId)) { - continue; - } - - if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId) - || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId, - info.authority)) { - continue; - } - - if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority) - == 0) { - continue; - } - - SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info); - for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) { - final Bundle extras = info.periodicSyncs.get(i).first; - final Long periodInMillis = info.periodicSyncs.get(i).second * 1000; - // find when this periodic sync was last scheduled to run - final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i); - - long remainingMillis - = periodInMillis - (shiftedNowAbsolute % periodInMillis); - - /* - * Sync scheduling strategy: - * Set the next periodic sync based on a random offset (in seconds). - * - * Also sync right now if any of the following cases hold - * and mark it as having been scheduled - * - * Case 1: This sync is ready to run now. - * Case 2: If the lastPollTimeAbsolute is in the future, - * sync now and reinitialize. This can happen for - * example if the user changed the time, synced and - * changed back. - * Case 3: If we failed to sync at the last scheduled time - */ - if (remainingMillis == periodInMillis // Case 1 - || lastPollTimeAbsolute > nowAbsolute // Case 2 - || (nowAbsolute - lastPollTimeAbsolute - >= periodInMillis)) { // Case 3 - // Sync now - final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( - info.account, info.userId, info.authority); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(info.authority, info.account.type), - info.userId); - if (syncAdapterInfo == null) { - continue; - } - scheduleSyncOperation( - new SyncOperation(info.account, info.userId, - SyncStorageEngine.SOURCE_PERIODIC, - info.authority, extras, 0 /* delay */, - backoff != null ? backoff.first : 0, - mSyncStorageEngine.getDelayUntilTime( - info.account, info.userId, info.authority), - syncAdapterInfo.type.allowParallelSyncs())); - status.setPeriodicSyncTime(i, nowAbsolute); - } - // Compute when this periodic sync should next run - final long nextPollTimeAbsolute = nowAbsolute + remainingMillis; - - // remember this time if it is earlier than earliestFuturePollTime - if (nextPollTimeAbsolute < earliestFuturePollTime) { - earliestFuturePollTime = nextPollTimeAbsolute; - } - } - } - - if (earliestFuturePollTime == Long.MAX_VALUE) { - return Long.MAX_VALUE; - } - - // convert absolute time to elapsed time - return SystemClock.elapsedRealtime() - + ((earliestFuturePollTime < nowAbsolute) - ? 0 - : (earliestFuturePollTime - nowAbsolute)); - } - - private long maybeStartNextSyncLocked() { - final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - if (isLoggable) Log.v(TAG, "maybeStartNextSync"); - - // If we aren't ready to run (e.g. the data connection is down), get out. - if (!mDataConnectionIsConnected) { - if (isLoggable) { - Log.v(TAG, "maybeStartNextSync: no data connection, skipping"); - } - return Long.MAX_VALUE; - } - - if (mStorageIsLow) { - if (isLoggable) { - Log.v(TAG, "maybeStartNextSync: memory low, skipping"); - } - return Long.MAX_VALUE; - } - - // If the accounts aren't known yet then we aren't ready to run. We will be kicked - // when the account lookup request does complete. - AccountAndUser[] accounts = mRunningAccounts; - if (accounts == INITIAL_ACCOUNTS_ARRAY) { - if (isLoggable) { - Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); - } - return Long.MAX_VALUE; - } - - // Otherwise consume SyncOperations from the head of the SyncQueue until one is - // found that is runnable (not disabled, etc). If that one is ready to run then - // start it, otherwise just get out. - final boolean backgroundDataUsageAllowed = - getConnectivityManager().getBackgroundDataSetting(); - - final long now = SystemClock.elapsedRealtime(); - - // will be set to the next time that a sync should be considered for running - long nextReadyToRunTime = Long.MAX_VALUE; - - // order the sync queue, dropping syncs that are not allowed - ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>(); - synchronized (mSyncQueue) { - if (isLoggable) { - Log.v(TAG, "build the operation array, syncQueue size is " - + mSyncQueue.getOperations().size()); - } - final Iterator<SyncOperation> operationIterator = mSyncQueue.getOperations() - .iterator(); - - final ActivityManager activityManager - = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - final Set<Integer> removedUsers = Sets.newHashSet(); - while (operationIterator.hasNext()) { - final SyncOperation op = operationIterator.next(); - - // drop the sync if the account of this operation no longer exists - if (!containsAccountAndUser(accounts, op.account, op.userId)) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - - // drop this sync request if it isn't syncable - int syncableState = mSyncStorageEngine.getIsSyncable( - op.account, op.userId, op.authority); - if (syncableState == 0) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - - // if the user in not running, drop the request - if (!activityManager.isUserRunning(op.userId)) { - final UserInfo userInfo = mUserManager.getUserInfo(op.userId); - if (userInfo == null) { - removedUsers.add(op.userId); - } - continue; - } - - // if the next run time is in the future, meaning there are no syncs ready - // to run, return the time - if (op.effectiveRunTime > now) { - if (nextReadyToRunTime > op.effectiveRunTime) { - nextReadyToRunTime = op.effectiveRunTime; - } - continue; - } - - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type), op.userId); - - // only proceed if network is connected for requesting UID - final boolean uidNetworkConnected; - if (syncAdapterInfo != null) { - final NetworkInfo networkInfo = getConnectivityManager() - .getActiveNetworkInfoForUid(syncAdapterInfo.uid); - uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); - } else { - uidNetworkConnected = false; - } - - // skip the sync if it isn't manual, and auto sync or - // background data usage is disabled or network is - // disconnected for the target UID. - if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) - && (syncableState > 0) - && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId) - || !backgroundDataUsageAllowed - || !uidNetworkConnected - || !mSyncStorageEngine.getSyncAutomatically( - op.account, op.userId, op.authority))) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - - operations.add(op); - } - for (Integer user : removedUsers) { - // if it's still removed - if (mUserManager.getUserInfo(user) == null) { - onUserRemoved(user); - } - } - } - - // find the next operation to dispatch, if one is ready - // iterate from the top, keep issuing (while potentially cancelling existing syncs) - // until the quotas are filled. - // once the quotas are filled iterate once more to find when the next one would be - // (also considering pre-emption reasons). - if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size()); - Collections.sort(operations); - if (isLoggable) Log.v(TAG, "dispatch all ready sync operations"); - for (int i = 0, N = operations.size(); i < N; i++) { - final SyncOperation candidate = operations.get(i); - final boolean candidateIsInitialization = candidate.isInitialization(); - - int numInit = 0; - int numRegular = 0; - ActiveSyncContext conflict = null; - ActiveSyncContext longRunning = null; - ActiveSyncContext toReschedule = null; - ActiveSyncContext oldestNonExpeditedRegular = null; - - for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final SyncOperation activeOp = activeSyncContext.mSyncOperation; - if (activeOp.isInitialization()) { - numInit++; - } else { - numRegular++; - if (!activeOp.isExpedited()) { - if (oldestNonExpeditedRegular == null - || (oldestNonExpeditedRegular.mStartTime - > activeSyncContext.mStartTime)) { - oldestNonExpeditedRegular = activeSyncContext; - } - } - } - if (activeOp.account.type.equals(candidate.account.type) - && activeOp.authority.equals(candidate.authority) - && activeOp.userId == candidate.userId - && (!activeOp.allowParallelSyncs - || activeOp.account.name.equals(candidate.account.name))) { - conflict = activeSyncContext; - // don't break out since we want to do a full count of the varieties - } else { - if (candidateIsInitialization == activeOp.isInitialization() - && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) { - longRunning = activeSyncContext; - // don't break out since we want to do a full count of the varieties - } - } - } - - if (isLoggable) { - Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate); - Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular); - Log.v(TAG, " longRunning: " + longRunning); - Log.v(TAG, " conflict: " + conflict); - Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular); - } - - final boolean roomAvailable = candidateIsInitialization - ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS - : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS; - - if (conflict != null) { - if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization() - && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) { - toReschedule = conflict; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an initialization " - + "takes higher priority, " + conflict); - } - } else if (candidate.expedited && !conflict.mSyncOperation.expedited - && (candidateIsInitialization - == conflict.mSyncOperation.isInitialization())) { - toReschedule = conflict; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an expedited " - + "takes higher priority, " + conflict); - } - } else { - continue; - } - } else if (roomAvailable) { - // dispatch candidate - } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null - && !candidateIsInitialization) { - // We found an active, non-expedited regular sync. We also know that the - // candidate doesn't conflict with this active sync since conflict - // is null. Reschedule the active sync and start the candidate. - toReschedule = oldestNonExpeditedRegular; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, " - + oldestNonExpeditedRegular); - } - } else if (longRunning != null - && (candidateIsInitialization - == longRunning.mSyncOperation.isInitialization())) { - // We found an active, long-running sync. Reschedule the active - // sync and start the candidate. - toReschedule = longRunning; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since it ran roo long, " - + longRunning); - } - } else { - // we were unable to find or make space to run this candidate, go on to - // the next one - continue; - } - - if (toReschedule != null) { - runSyncFinishedOrCanceledLocked(null, toReschedule); - scheduleSyncOperation(toReschedule.mSyncOperation); - } - synchronized (mSyncQueue) { - mSyncQueue.remove(candidate); - } - dispatchSyncOperation(candidate); - } - - return nextReadyToRunTime; - } - - private boolean dispatchSyncOperation(SyncOperation op) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op); - Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); - for (ActiveSyncContext syncContext : mActiveSyncContexts) { - Log.v(TAG, syncContext.toString()); - } - } - - // connect to the sync adapter - SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId); - if (syncAdapterInfo == null) { - Log.d(TAG, "can't find a sync adapter for " + syncAdapterType - + ", removing settings for it"); - mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority); - return false; - } - - ActiveSyncContext activeSyncContext = - new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid); - activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); - mActiveSyncContexts.add(activeSyncContext); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); - } - if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) { - Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); - closeActiveSyncContext(activeSyncContext); - return false; - } - - return true; - } - - private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext, - ISyncAdapter syncAdapter) { - activeSyncContext.mSyncAdapter = syncAdapter; - final SyncOperation syncOperation = activeSyncContext.mSyncOperation; - try { - activeSyncContext.mIsLinkedToDeath = true; - syncAdapter.asBinder().linkToDeath(activeSyncContext, 0); - - syncAdapter.startSync(activeSyncContext, syncOperation.authority, - syncOperation.account, syncOperation.extras); - } catch (RemoteException remoteExc) { - Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); - closeActiveSyncContext(activeSyncContext); - increaseBackoffSetting(syncOperation); - scheduleSyncOperation(new SyncOperation(syncOperation)); - } catch (RuntimeException exc) { - closeActiveSyncContext(activeSyncContext); - Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); - } - } - - private void cancelActiveSyncLocked(Account account, int userId, String authority) { - ArrayList<ActiveSyncContext> activeSyncs = - new ArrayList<ActiveSyncContext>(mActiveSyncContexts); - for (ActiveSyncContext activeSyncContext : activeSyncs) { - if (activeSyncContext != null) { - // if an account was specified then only cancel the sync if it matches - if (account != null) { - if (!account.equals(activeSyncContext.mSyncOperation.account)) { - continue; - } - } - // if an authority was specified then only cancel the sync if it matches - if (authority != null) { - if (!authority.equals(activeSyncContext.mSyncOperation.authority)) { - continue; - } - } - // check if the userid matches - if (userId != UserHandle.USER_ALL - && userId != activeSyncContext.mSyncOperation.userId) { - continue; - } - runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */, - activeSyncContext); - } - } - } - - private void runSyncFinishedOrCanceledLocked(SyncResult syncResult, - ActiveSyncContext activeSyncContext) { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - - if (activeSyncContext.mIsLinkedToDeath) { - activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); - activeSyncContext.mIsLinkedToDeath = false; - } - closeActiveSyncContext(activeSyncContext); - - final SyncOperation syncOperation = activeSyncContext.mSyncOperation; - - final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; - - String historyMessage; - int downstreamActivity; - int upstreamActivity; - if (syncResult != null) { - if (isLoggable) { - Log.v(TAG, "runSyncFinishedOrCanceled [finished]: " - + syncOperation + ", result " + syncResult); - } - - if (!syncResult.hasError()) { - historyMessage = SyncStorageEngine.MESG_SUCCESS; - // TODO: set these correctly when the SyncResult is extended to include it - downstreamActivity = 0; - upstreamActivity = 0; - clearBackoffSetting(syncOperation); - } else { - Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); - // the operation failed so increase the backoff time - if (!syncResult.syncAlreadyInProgress) { - increaseBackoffSetting(syncOperation); - } - // reschedule the sync if so indicated by the syncResult - maybeRescheduleSync(syncResult, syncOperation); - historyMessage = Integer.toString(syncResultToErrorNumber(syncResult)); - // TODO: set these correctly when the SyncResult is extended to include it - downstreamActivity = 0; - upstreamActivity = 0; - } - - setDelayUntilTime(syncOperation, syncResult.delayUntil); - } else { - if (isLoggable) { - Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); - } - if (activeSyncContext.mSyncAdapter != null) { - try { - activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); - } catch (RemoteException e) { - // we don't need to retry this in this case - } - } - historyMessage = SyncStorageEngine.MESG_CANCELED; - downstreamActivity = 0; - upstreamActivity = 0; - } - - stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, - upstreamActivity, downstreamActivity, elapsedTime); - - if (syncResult != null && syncResult.tooManyDeletions) { - installHandleTooManyDeletesNotification(syncOperation.account, - syncOperation.authority, syncResult.stats.numDeletes, - syncOperation.userId); - } else { - mNotificationMgr.cancelAsUser(null, - syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(), - new UserHandle(syncOperation.userId)); - } - - if (syncResult != null && syncResult.fullSyncRequested) { - scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId, - syncOperation.syncSource, syncOperation.authority, new Bundle(), 0, - syncOperation.backoff, syncOperation.delayUntil, - syncOperation.allowParallelSyncs)); - } - // no need to schedule an alarm, as that will be done by our caller. - } - - private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { - activeSyncContext.close(); - mActiveSyncContexts.remove(activeSyncContext); - mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, - activeSyncContext.mSyncOperation.userId); - } - - /** - * Convert the error-containing SyncResult into the Sync.History error number. Since - * the SyncResult may indicate multiple errors at once, this method just returns the - * most "serious" error. - * @param syncResult the SyncResult from which to read - * @return the most "serious" error set in the SyncResult - * @throws IllegalStateException if the SyncResult does not indicate any errors. - * If SyncResult.error() is true then it is safe to call this. - */ - private int syncResultToErrorNumber(SyncResult syncResult) { - if (syncResult.syncAlreadyInProgress) - return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; - if (syncResult.stats.numAuthExceptions > 0) - return ContentResolver.SYNC_ERROR_AUTHENTICATION; - if (syncResult.stats.numIoExceptions > 0) - return ContentResolver.SYNC_ERROR_IO; - if (syncResult.stats.numParseExceptions > 0) - return ContentResolver.SYNC_ERROR_PARSE; - if (syncResult.stats.numConflictDetectedExceptions > 0) - return ContentResolver.SYNC_ERROR_CONFLICT; - if (syncResult.tooManyDeletions) - return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; - if (syncResult.tooManyRetries) - return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; - if (syncResult.databaseError) - return ContentResolver.SYNC_ERROR_INTERNAL; - throw new IllegalStateException("we are not in an error state, " + syncResult); - } - - private void manageSyncNotificationLocked() { - boolean shouldCancel; - boolean shouldInstall; - - if (mActiveSyncContexts.isEmpty()) { - mSyncNotificationInfo.startTime = null; - - // we aren't syncing. if the notification is active then remember that we need - // to cancel it and then clear out the info - shouldCancel = mSyncNotificationInfo.isActive; - shouldInstall = false; - } else { - // we are syncing - final long now = SystemClock.elapsedRealtime(); - if (mSyncNotificationInfo.startTime == null) { - mSyncNotificationInfo.startTime = now; - } - - // there are three cases: - // - the notification is up: do nothing - // - the notification is not up but it isn't time yet: don't install - // - the notification is not up and it is time: need to install - - if (mSyncNotificationInfo.isActive) { - shouldInstall = shouldCancel = false; - } else { - // it isn't currently up, so there is nothing to cancel - shouldCancel = false; - - final boolean timeToShowNotification = - now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; - if (timeToShowNotification) { - shouldInstall = true; - } else { - // show the notification immediately if this is a manual sync - shouldInstall = false; - for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final boolean manualSync = activeSyncContext.mSyncOperation.extras - .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - if (manualSync) { - shouldInstall = true; - break; - } - } - } - } - } - - if (shouldCancel && !shouldInstall) { - mNeedSyncActiveNotification = false; - sendSyncStateIntent(); - mSyncNotificationInfo.isActive = false; - } - - if (shouldInstall) { - mNeedSyncActiveNotification = true; - sendSyncStateIntent(); - mSyncNotificationInfo.isActive = true; - } - } - - private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime, - long nextPendingEventElapsedTime) { - // in each of these cases the sync loop will be kicked, which will cause this - // method to be called again - if (!mDataConnectionIsConnected) return; - if (mStorageIsLow) return; - - // When the status bar notification should be raised - final long notificationTime = - (!mSyncHandler.mSyncNotificationInfo.isActive - && mSyncHandler.mSyncNotificationInfo.startTime != null) - ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY - : Long.MAX_VALUE; - - // When we should consider canceling an active sync - long earliestTimeoutTime = Long.MAX_VALUE; - for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - final long currentSyncTimeoutTime = - currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is " - + currentSyncTimeoutTime); - } - if (earliestTimeoutTime > currentSyncTimeoutTime) { - earliestTimeoutTime = currentSyncTimeoutTime; - } - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is " - + nextPeriodicEventElapsedTime); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is " - + nextPendingEventElapsedTime); - } - - long alarmTime = Math.min(notificationTime, earliestTimeoutTime); - alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime); - alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime); - - // Bound the alarm time. - final long now = SystemClock.elapsedRealtime(); - if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, " - + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); - } - alarmTime = now + SYNC_ALARM_TIMEOUT_MIN; - } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, " - + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); - } - alarmTime = now + SYNC_ALARM_TIMEOUT_MAX; - } - - // determine if we need to set or cancel the alarm - boolean shouldSet = false; - boolean shouldCancel = false; - final boolean alarmIsActive = mAlarmScheduleTime != null; - final boolean needAlarm = alarmTime != Long.MAX_VALUE; - if (needAlarm) { - if (!alarmIsActive || alarmTime < mAlarmScheduleTime) { - shouldSet = true; - } - } else { - shouldCancel = alarmIsActive; - } - - // set or cancel the alarm as directed - ensureAlarmService(); - if (shouldSet) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time " - + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000) - + " secs from now"); - } - mAlarmScheduleTime = alarmTime; - mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, - mSyncAlarmIntent); - } else if (shouldCancel) { - mAlarmScheduleTime = null; - mAlarmService.cancel(mSyncAlarmIntent); - } - } - - private void sendSyncStateIntent() { - Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED); - syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - syncStateIntent.putExtra("active", mNeedSyncActiveNotification); - syncStateIntent.putExtra("failing", false); - mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER); - } - - private void installHandleTooManyDeletesNotification(Account account, String authority, - long numDeletes, int userId) { - if (mNotificationMgr == null) return; - - final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( - authority, 0 /* flags */); - if (providerInfo == null) { - return; - } - CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); - - Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); - clickIntent.putExtra("account", account); - clickIntent.putExtra("authority", authority); - clickIntent.putExtra("provider", authorityName.toString()); - clickIntent.putExtra("numDeletes", numDeletes); - - if (!isActivityAvailable(clickIntent)) { - Log.w(TAG, "No activity found to handle too many deletes."); - return; - } - - final PendingIntent pendingIntent = PendingIntent - .getActivityAsUser(mContext, 0, clickIntent, - PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId)); - - CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( - R.string.contentServiceTooManyDeletesNotificationDesc); - - Notification notification = - new Notification(R.drawable.stat_notify_sync_error, - mContext.getString(R.string.contentServiceSync), - System.currentTimeMillis()); - notification.setLatestEventInfo(mContext, - mContext.getString(R.string.contentServiceSyncNotificationTitle), - String.format(tooManyDeletesDescFormat.toString(), authorityName), - pendingIntent); - notification.flags |= Notification.FLAG_ONGOING_EVENT; - mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), - notification, new UserHandle(userId)); - } - - /** - * Checks whether an activity exists on the system image for the given intent. - * - * @param intent The intent for an activity. - * @return Whether or not an activity exists. - */ - private boolean isActivityAvailable(Intent intent) { - PackageManager pm = mContext.getPackageManager(); - List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); - int listSize = list.size(); - for (int i = 0; i < listSize; i++) { - ResolveInfo resolveInfo = list.get(i); - if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) - != 0) { - return true; - } - } - - return false; - } - - public long insertStartSyncEvent(SyncOperation syncOperation) { - final int source = syncOperation.syncSource; - final long now = System.currentTimeMillis(); - - EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_START, source, - syncOperation.account.name.hashCode()); - - return mSyncStorageEngine.insertStartSyncEvent( - syncOperation.account, syncOperation.userId, syncOperation.authority, - now, source, syncOperation.isInitialization()); - } - - public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, - int upstreamActivity, int downstreamActivity, long elapsedTime) { - EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_STOP, syncOperation.syncSource, - syncOperation.account.name.hashCode()); - - mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, - resultMessage, downstreamActivity, upstreamActivity); - } - } - - private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) { - for (ActiveSyncContext sync : mActiveSyncContexts) { - if (sync == activeSyncContext) { - return true; - } - } - return false; - } -} diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java deleted file mode 100644 index 6611fcd..0000000 --- a/core/java/android/content/SyncOperation.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.os.Bundle; -import android.os.SystemClock; - -/** - * Value type that represents a sync operation. - * @hide - */ -public class SyncOperation implements Comparable { - public final Account account; - public final int userId; - public int syncSource; - public String authority; - public final boolean allowParallelSyncs; - public Bundle extras; - public final String key; - public long earliestRunTime; - public boolean expedited; - public SyncStorageEngine.PendingOperation pendingOperation; - public Long backoff; - public long delayUntil; - public long effectiveRunTime; - - public SyncOperation(Account account, int userId, int source, String authority, Bundle extras, - long delayInMs, long backoff, long delayUntil, boolean allowParallelSyncs) { - this.account = account; - this.userId = userId; - this.syncSource = source; - this.authority = authority; - this.allowParallelSyncs = allowParallelSyncs; - this.extras = new Bundle(extras); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_UPLOAD); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_MANUAL); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS); - this.delayUntil = delayUntil; - this.backoff = backoff; - final long now = SystemClock.elapsedRealtime(); - if (delayInMs < 0) { - this.expedited = true; - this.earliestRunTime = now; - } else { - this.expedited = false; - this.earliestRunTime = now + delayInMs; - } - updateEffectiveRunTime(); - this.key = toKey(); - } - - private void removeFalseExtra(String extraName) { - if (!extras.getBoolean(extraName, false)) { - extras.remove(extraName); - } - } - - SyncOperation(SyncOperation other) { - this.account = other.account; - this.userId = other.userId; - this.syncSource = other.syncSource; - this.authority = other.authority; - this.extras = new Bundle(other.extras); - this.expedited = other.expedited; - this.earliestRunTime = SystemClock.elapsedRealtime(); - this.backoff = other.backoff; - this.delayUntil = other.delayUntil; - this.allowParallelSyncs = other.allowParallelSyncs; - this.updateEffectiveRunTime(); - this.key = toKey(); - } - - public String toString() { - return dump(true); - } - - public String dump(boolean useOneLine) { - StringBuilder sb = new StringBuilder() - .append(account.name) - .append(" u") - .append(userId).append(" (") - .append(account.type) - .append(")") - .append(", ") - .append(authority) - .append(", ") - .append(SyncStorageEngine.SOURCES[syncSource]) - .append(", earliestRunTime ") - .append(earliestRunTime); - if (expedited) { - sb.append(", EXPEDITED"); - } - if (!useOneLine && !extras.keySet().isEmpty()) { - sb.append("\n "); - extrasToStringBuilder(extras, sb); - } - return sb.toString(); - } - - public boolean isInitialization() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false); - } - - public boolean isExpedited() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); - } - - public boolean ignoreBackoff() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false); - } - - private String toKey() { - StringBuilder sb = new StringBuilder(); - sb.append("authority: ").append(authority); - sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type - + "}"); - sb.append(" extras: "); - extrasToStringBuilder(extras, sb); - return sb.toString(); - } - - public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) { - sb.append("["); - for (String key : bundle.keySet()) { - sb.append(key).append("=").append(bundle.get(key)).append(" "); - } - sb.append("]"); - } - - public void updateEffectiveRunTime() { - effectiveRunTime = ignoreBackoff() - ? earliestRunTime - : Math.max( - Math.max(earliestRunTime, delayUntil), - backoff); - } - - public int compareTo(Object o) { - SyncOperation other = (SyncOperation)o; - - if (expedited != other.expedited) { - return expedited ? -1 : 1; - } - - if (effectiveRunTime == other.effectiveRunTime) { - return 0; - } - - return effectiveRunTime < other.effectiveRunTime ? -1 : 1; - } -} diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java deleted file mode 100644 index c9a325e..0000000 --- a/core/java/android/content/SyncQueue.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.content.pm.RegisteredServicesCache.ServiceInfo; -import android.os.SystemClock; -import android.os.UserHandle; -import android.text.format.DateUtils; -import android.util.Log; -import android.util.Pair; - -import com.google.android.collect.Maps; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * Queue of pending sync operations. Not inherently thread safe, external - * callers are responsible for locking. - * - * @hide - */ -public class SyncQueue { - private static final String TAG = "SyncManager"; - - private final SyncStorageEngine mSyncStorageEngine; - private final SyncAdaptersCache mSyncAdapters; - - // A Map of SyncOperations operationKey -> SyncOperation that is designed for - // quick lookup of an enqueued SyncOperation. - private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap(); - - public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) { - mSyncStorageEngine = syncStorageEngine; - mSyncAdapters = syncAdapters; - } - - public void addPendingOperations(int userId) { - for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) { - if (op.userId != userId) continue; - - final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( - op.account, op.userId, op.authority); - final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type), op.userId); - if (syncAdapterInfo == null) { - Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId " - + op.userId); - continue; - } - SyncOperation syncOperation = new SyncOperation( - op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */, - backoff != null ? backoff.first : 0, - mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), - syncAdapterInfo.type.allowParallelSyncs()); - syncOperation.expedited = op.expedited; - syncOperation.pendingOperation = op; - add(syncOperation, op); - } - } - - public boolean add(SyncOperation operation) { - return add(operation, null /* this is not coming from the database */); - } - - private boolean add(SyncOperation operation, - SyncStorageEngine.PendingOperation pop) { - // - if an operation with the same key exists and this one should run earlier, - // update the earliestRunTime of the existing to the new time - // - if an operation with the same key exists and if this one should run - // later, ignore it - // - if no operation exists then add the new one - final String operationKey = operation.key; - final SyncOperation existingOperation = mOperationsMap.get(operationKey); - - if (existingOperation != null) { - boolean changed = false; - if (existingOperation.expedited == operation.expedited) { - final long newRunTime = - Math.min(existingOperation.earliestRunTime, operation.earliestRunTime); - if (existingOperation.earliestRunTime != newRunTime) { - existingOperation.earliestRunTime = newRunTime; - changed = true; - } - } else { - if (operation.expedited) { - existingOperation.expedited = true; - changed = true; - } - } - return changed; - } - - operation.pendingOperation = pop; - if (operation.pendingOperation == null) { - pop = new SyncStorageEngine.PendingOperation( - operation.account, operation.userId, operation.syncSource, - operation.authority, operation.extras, operation.expedited); - pop = mSyncStorageEngine.insertIntoPending(pop); - if (pop == null) { - throw new IllegalStateException("error adding pending sync operation " - + operation); - } - operation.pendingOperation = pop; - } - - mOperationsMap.put(operationKey, operation); - return true; - } - - public void removeUser(int userId) { - ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>(); - for (SyncOperation op : mOperationsMap.values()) { - if (op.userId == userId) { - opsToRemove.add(op); - } - } - - for (SyncOperation op : opsToRemove) { - remove(op); - } - } - - /** - * Remove the specified operation if it is in the queue. - * @param operation the operation to remove - */ - public void remove(SyncOperation operation) { - SyncOperation operationToRemove = mOperationsMap.remove(operation.key); - if (operationToRemove == null) { - return; - } - if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) { - final String errorMessage = "unable to find pending row for " + operationToRemove; - Log.e(TAG, errorMessage, new IllegalStateException(errorMessage)); - } - } - - public void onBackoffChanged(Account account, int userId, String providerName, long backoff) { - // for each op that matches the account and provider update its - // backoff and effectiveStartTime - for (SyncOperation op : mOperationsMap.values()) { - if (op.account.equals(account) && op.authority.equals(providerName) - && op.userId == userId) { - op.backoff = backoff; - op.updateEffectiveRunTime(); - } - } - } - - public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) { - // for each op that matches the account and provider update its - // delayUntilTime and effectiveStartTime - for (SyncOperation op : mOperationsMap.values()) { - if (op.account.equals(account) && op.authority.equals(providerName)) { - op.delayUntil = delayUntil; - op.updateEffectiveRunTime(); - } - } - } - - public void remove(Account account, int userId, String authority) { - Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry<String, SyncOperation> entry = entries.next(); - SyncOperation syncOperation = entry.getValue(); - if (account != null && !syncOperation.account.equals(account)) { - continue; - } - if (authority != null && !syncOperation.authority.equals(authority)) { - continue; - } - if (userId != syncOperation.userId) { - continue; - } - entries.remove(); - if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) { - final String errorMessage = "unable to find pending row for " + syncOperation; - Log.e(TAG, errorMessage, new IllegalStateException(errorMessage)); - } - } - } - - public Collection<SyncOperation> getOperations() { - return mOperationsMap.values(); - } - - public void dump(StringBuilder sb) { - final long now = SystemClock.elapsedRealtime(); - sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n"); - for (SyncOperation operation : mOperationsMap.values()) { - sb.append(" "); - if (operation.effectiveRunTime <= now) { - sb.append("READY"); - } else { - sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000)); - } - sb.append(" - "); - sb.append(operation.dump(false)).append("\n"); - } - } -} diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java index bb2b2da..ff628d9 100644 --- a/core/java/android/content/SyncStatusInfo.java +++ b/core/java/android/content/SyncStatusInfo.java @@ -46,19 +46,18 @@ public class SyncStatusInfo implements Parcelable { private static final String TAG = "Sync"; - SyncStatusInfo(int authorityId) { + public SyncStatusInfo(int authorityId) { this.authorityId = authorityId; } public int getLastFailureMesgAsInt(int def) { - try { - if (lastFailureMesg != null) { - return Integer.parseInt(lastFailureMesg); - } - } catch (NumberFormatException e) { - Log.d(TAG, "error parsing lastFailureMesg of " + lastFailureMesg, e); + final int i = ContentResolver.syncErrorStringToInt(lastFailureMesg); + if (i > 0) { + return i; + } else { + Log.d(TAG, "Unknown lastFailureMesg:" + lastFailureMesg); + return def; } - return def; } public int describeContents() { @@ -92,7 +91,7 @@ public class SyncStatusInfo implements Parcelable { } } - SyncStatusInfo(Parcel parcel) { + public SyncStatusInfo(Parcel parcel) { int version = parcel.readInt(); if (version != VERSION && version != 1) { Log.w("SyncStatusInfo", "Unknown version: " + version); diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java deleted file mode 100644 index 1ecab09..0000000 --- a/core/java/android/content/SyncStorageEngine.java +++ /dev/null @@ -1,2303 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import android.accounts.Account; -import android.accounts.AccountAndUser; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteQueryBuilder; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.Message; -import android.os.Parcel; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.AtomicFile; -import android.util.Log; -import android.util.SparseArray; -import android.util.Xml; -import android.util.Pair; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Random; -import java.util.TimeZone; -import java.util.List; - -/** - * Singleton that tracks the sync data and overall sync - * history on the device. - * - * @hide - */ -public class SyncStorageEngine extends Handler { - - private static final String TAG = "SyncManager"; - private static final boolean DEBUG = false; - private static final boolean DEBUG_FILE = false; - - private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId"; - private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles"; - private static final String XML_ATTR_SYNC_RANDOM_OFFSET = "offsetInSeconds"; - private static final String XML_ATTR_ENABLED = "enabled"; - private static final String XML_ATTR_USER = "user"; - private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles"; - - private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day - - @VisibleForTesting - static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4; - - /** Enum value for a sync start event. */ - public static final int EVENT_START = 0; - - /** Enum value for a sync stop event. */ - public static final int EVENT_STOP = 1; - - // TODO: i18n -- grab these out of resources. - /** String names for the sync event types. */ - public static final String[] EVENTS = { "START", "STOP" }; - - /** Enum value for a server-initiated sync. */ - public static final int SOURCE_SERVER = 0; - - /** Enum value for a local-initiated sync. */ - public static final int SOURCE_LOCAL = 1; - /** - * Enum value for a poll-based sync (e.g., upon connection to - * network) - */ - public static final int SOURCE_POLL = 2; - - /** Enum value for a user-initiated sync. */ - public static final int SOURCE_USER = 3; - - /** Enum value for a periodic sync. */ - public static final int SOURCE_PERIODIC = 4; - - public static final long NOT_IN_BACKOFF_MODE = -1; - - public static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT = - new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED"); - - // TODO: i18n -- grab these out of resources. - /** String names for the sync source types. */ - public static final String[] SOURCES = { "SERVER", - "LOCAL", - "POLL", - "USER", - "PERIODIC" }; - - // The MESG column will contain one of these or one of the Error types. - public static final String MESG_SUCCESS = "success"; - public static final String MESG_CANCELED = "canceled"; - - public static final int MAX_HISTORY = 100; - - private static final int MSG_WRITE_STATUS = 1; - private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes - - private static final int MSG_WRITE_STATISTICS = 2; - private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour - - private static final boolean SYNC_ENABLED_DEFAULT = false; - - // the version of the accounts xml file format - private static final int ACCOUNTS_VERSION = 2; - - private static HashMap<String, String> sAuthorityRenames; - - static { - sAuthorityRenames = new HashMap<String, String>(); - sAuthorityRenames.put("contacts", "com.android.contacts"); - sAuthorityRenames.put("calendar", "com.android.calendar"); - } - - public static class PendingOperation { - final Account account; - final int userId; - final int syncSource; - final String authority; - final Bundle extras; // note: read-only. - final boolean expedited; - - int authorityId; - byte[] flatExtras; - - PendingOperation(Account account, int userId, int source, - String authority, Bundle extras, boolean expedited) { - this.account = account; - this.userId = userId; - this.syncSource = source; - this.authority = authority; - this.extras = extras != null ? new Bundle(extras) : extras; - this.expedited = expedited; - this.authorityId = -1; - } - - PendingOperation(PendingOperation other) { - this.account = other.account; - this.userId = other.userId; - this.syncSource = other.syncSource; - this.authority = other.authority; - this.extras = other.extras; - this.authorityId = other.authorityId; - this.expedited = other.expedited; - } - } - - static class AccountInfo { - final AccountAndUser accountAndUser; - final HashMap<String, AuthorityInfo> authorities = - new HashMap<String, AuthorityInfo>(); - - AccountInfo(AccountAndUser accountAndUser) { - this.accountAndUser = accountAndUser; - } - } - - public static class AuthorityInfo { - final Account account; - final int userId; - final String authority; - final int ident; - boolean enabled; - int syncable; - long backoffTime; - long backoffDelay; - long delayUntil; - final ArrayList<Pair<Bundle, Long>> periodicSyncs; - - /** - * Copy constructor for making deep-ish copies. Only the bundles stored - * in periodic syncs can make unexpected changes. - * - * @param toCopy AuthorityInfo to be copied. - */ - AuthorityInfo(AuthorityInfo toCopy) { - account = toCopy.account; - userId = toCopy.userId; - authority = toCopy.authority; - ident = toCopy.ident; - enabled = toCopy.enabled; - syncable = toCopy.syncable; - backoffTime = toCopy.backoffTime; - backoffDelay = toCopy.backoffDelay; - delayUntil = toCopy.delayUntil; - periodicSyncs = new ArrayList<Pair<Bundle, Long>>(); - for (Pair<Bundle, Long> sync : toCopy.periodicSyncs) { - // Still not a perfect copy, because we are just copying the mappings. - periodicSyncs.add(Pair.create(new Bundle(sync.first), sync.second)); - } - } - - AuthorityInfo(Account account, int userId, String authority, int ident) { - this.account = account; - this.userId = userId; - this.authority = authority; - this.ident = ident; - enabled = SYNC_ENABLED_DEFAULT; - syncable = -1; // default to "unknown" - backoffTime = -1; // if < 0 then we aren't in backoff mode - backoffDelay = -1; // if < 0 then we aren't in backoff mode - periodicSyncs = new ArrayList<Pair<Bundle, Long>>(); - periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS)); - } - } - - public static class SyncHistoryItem { - int authorityId; - int historyId; - long eventTime; - long elapsedTime; - int source; - int event; - long upstreamActivity; - long downstreamActivity; - String mesg; - boolean initialization; - } - - public static class DayStats { - public final int day; - public int successCount; - public long successTime; - public int failureCount; - public long failureTime; - - public DayStats(int day) { - this.day = day; - } - } - - interface OnSyncRequestListener { - /** - * Called when a sync is needed on an account(s) due to some change in state. - * @param account - * @param userId - * @param authority - * @param extras - */ - public void onSyncRequest(Account account, int userId, String authority, Bundle extras); - } - - // Primary list of all syncable authorities. Also our global lock. - private final SparseArray<AuthorityInfo> mAuthorities = - new SparseArray<AuthorityInfo>(); - - private final HashMap<AccountAndUser, AccountInfo> mAccounts - = new HashMap<AccountAndUser, AccountInfo>(); - - private final ArrayList<PendingOperation> mPendingOperations = - new ArrayList<PendingOperation>(); - - private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs - = new SparseArray<ArrayList<SyncInfo>>(); - - private final SparseArray<SyncStatusInfo> mSyncStatus = - new SparseArray<SyncStatusInfo>(); - - private final ArrayList<SyncHistoryItem> mSyncHistory = - new ArrayList<SyncHistoryItem>(); - - private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners - = new RemoteCallbackList<ISyncStatusObserver>(); - - private int mNextAuthorityId = 0; - - // We keep 4 weeks of stats. - private final DayStats[] mDayStats = new DayStats[7*4]; - private final Calendar mCal; - private int mYear; - private int mYearInDays; - - private final Context mContext; - - private static volatile SyncStorageEngine sSyncStorageEngine = null; - - private int mSyncRandomOffset; - - /** - * This file contains the core engine state: all accounts and the - * settings for them. It must never be lost, and should be changed - * infrequently, so it is stored as an XML file. - */ - private final AtomicFile mAccountInfoFile; - - /** - * This file contains the current sync status. We would like to retain - * it across boots, but its loss is not the end of the world, so we store - * this information as binary data. - */ - private final AtomicFile mStatusFile; - - /** - * This file contains sync statistics. This is purely debugging information - * so is written infrequently and can be thrown away at any time. - */ - private final AtomicFile mStatisticsFile; - - /** - * This file contains the pending sync operations. It is a binary file, - * which must be updated every time an operation is added or removed, - * so we have special handling of it. - */ - private final AtomicFile mPendingFile; - private static final int PENDING_FINISH_TO_WRITE = 4; - private int mNumPendingFinished = 0; - - private int mNextHistoryId = 0; - private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>(); - private boolean mDefaultMasterSyncAutomatically; - - private OnSyncRequestListener mSyncRequestListener; - - private SyncStorageEngine(Context context, File dataDir) { - mContext = context; - sSyncStorageEngine = this; - - mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); - - mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically); - - File systemDir = new File(dataDir, "system"); - File syncDir = new File(systemDir, "sync"); - syncDir.mkdirs(); - mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - mStatusFile = new AtomicFile(new File(syncDir, "status.bin")); - mPendingFile = new AtomicFile(new File(syncDir, "pending.bin")); - mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin")); - - readAccountInfoLocked(); - readStatusLocked(); - readPendingOperationsLocked(); - readStatisticsLocked(); - readAndDeleteLegacyAccountInfoLocked(); - writeAccountInfoLocked(); - writeStatusLocked(); - writePendingOperationsLocked(); - writeStatisticsLocked(); - } - - public static SyncStorageEngine newTestInstance(Context context) { - return new SyncStorageEngine(context, context.getFilesDir()); - } - - public static void init(Context context) { - if (sSyncStorageEngine != null) { - return; - } - // This call will return the correct directory whether Encrypted File Systems is - // enabled or not. - File dataDir = Environment.getSecureDataDirectory(); - sSyncStorageEngine = new SyncStorageEngine(context, dataDir); - } - - public static SyncStorageEngine getSingleton() { - if (sSyncStorageEngine == null) { - throw new IllegalStateException("not initialized"); - } - return sSyncStorageEngine; - } - - protected void setOnSyncRequestListener(OnSyncRequestListener listener) { - if (mSyncRequestListener == null) { - mSyncRequestListener = listener; - } - } - - @Override public void handleMessage(Message msg) { - if (msg.what == MSG_WRITE_STATUS) { - synchronized (mAuthorities) { - writeStatusLocked(); - } - } else if (msg.what == MSG_WRITE_STATISTICS) { - synchronized (mAuthorities) { - writeStatisticsLocked(); - } - } - } - - public int getSyncRandomOffset() { - return mSyncRandomOffset; - } - - public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { - synchronized (mAuthorities) { - mChangeListeners.register(callback, mask); - } - } - - public void removeStatusChangeListener(ISyncStatusObserver callback) { - synchronized (mAuthorities) { - mChangeListeners.unregister(callback); - } - } - - private void reportChange(int which) { - ArrayList<ISyncStatusObserver> reports = null; - synchronized (mAuthorities) { - int i = mChangeListeners.beginBroadcast(); - while (i > 0) { - i--; - Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i); - if ((which & mask.intValue()) == 0) { - continue; - } - if (reports == null) { - reports = new ArrayList<ISyncStatusObserver>(i); - } - reports.add(mChangeListeners.getBroadcastItem(i)); - } - mChangeListeners.finishBroadcast(); - } - - if (DEBUG) { - Log.v(TAG, "reportChange " + which + " to: " + reports); - } - - if (reports != null) { - int i = reports.size(); - while (i > 0) { - i--; - try { - reports.get(i).onStatusChanged(which); - } catch (RemoteException e) { - // The remote callback list will take care of this for us. - } - } - } - } - - public boolean getSyncAutomatically(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getSyncAutomatically"); - return authority != null && authority.enabled; - } - - int i = mAuthorities.size(); - while (i > 0) { - i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(providerName) - && authority.userId == userId - && authority.enabled) { - return true; - } - } - return false; - } - } - - public void setSyncAutomatically(Account account, int userId, String providerName, - boolean sync) { - if (DEBUG) { - Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName - + ", user " + userId + " -> " + sync); - } - synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, - false); - if (authority.enabled == sync) { - if (DEBUG) { - Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); - } - return; - } - authority.enabled = sync; - writeAccountInfoLocked(); - } - - if (sync) { - requestSync(account, userId, providerName, new Bundle()); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public int getIsSyncable(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getIsSyncable"); - if (authority == null) { - return -1; - } - return authority.syncable; - } - - int i = mAuthorities.size(); - while (i > 0) { - i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(providerName)) { - return authority.syncable; - } - } - return -1; - } - } - - public void setIsSyncable(Account account, int userId, String providerName, int syncable) { - if (syncable > 1) { - syncable = 1; - } else if (syncable < -1) { - syncable = -1; - } - if (DEBUG) { - Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName - + ", user " + userId + " -> " + syncable); - } - synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, - false); - if (authority.syncable == syncable) { - if (DEBUG) { - Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); - } - return; - } - authority.syncable = syncable; - writeAccountInfoLocked(); - } - - if (syncable > 0) { - requestSync(account, userId, providerName, new Bundle()); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getBackoff"); - if (authority == null || authority.backoffTime < 0) { - return null; - } - return Pair.create(authority.backoffTime, authority.backoffDelay); - } - } - - public void setBackoff(Account account, int userId, String providerName, - long nextSyncTime, long nextDelay) { - if (DEBUG) { - Log.v(TAG, "setBackoff: " + account + ", provider " + providerName - + ", user " + userId - + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay); - } - boolean changed = false; - synchronized (mAuthorities) { - if (account == null || providerName == null) { - for (AccountInfo accountInfo : mAccounts.values()) { - if (account != null && !account.equals(accountInfo.accountAndUser.account) - && userId != accountInfo.accountAndUser.userId) { - continue; - } - for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { - if (providerName != null && !providerName.equals(authorityInfo.authority)) { - continue; - } - if (authorityInfo.backoffTime != nextSyncTime - || authorityInfo.backoffDelay != nextDelay) { - authorityInfo.backoffTime = nextSyncTime; - authorityInfo.backoffDelay = nextDelay; - changed = true; - } - } - } - } else { - AuthorityInfo authority = - getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */, - true); - if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) { - return; - } - authority.backoffTime = nextSyncTime; - authority.backoffDelay = nextDelay; - changed = true; - } - } - - if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - } - - public void clearAllBackoffs(SyncQueue syncQueue) { - boolean changed = false; - synchronized (mAuthorities) { - synchronized (syncQueue) { - for (AccountInfo accountInfo : mAccounts.values()) { - for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { - if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE - || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { - if (DEBUG) { - Log.v(TAG, "clearAllBackoffs:" - + " authority:" + authorityInfo.authority - + " account:" + accountInfo.accountAndUser.account.name - + " user:" + accountInfo.accountAndUser.userId - + " backoffTime was: " + authorityInfo.backoffTime - + " backoffDelay was: " + authorityInfo.backoffDelay); - } - authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; - authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; - syncQueue.onBackoffChanged(accountInfo.accountAndUser.account, - accountInfo.accountAndUser.userId, authorityInfo.authority, 0); - changed = true; - } - } - } - } - } - - if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - } - - public void setDelayUntilTime(Account account, int userId, String providerName, - long delayUntil) { - if (DEBUG) { - Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName - + ", user " + userId + " -> delayUntil " + delayUntil); - } - synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked( - account, userId, providerName, -1 /* ident */, true); - if (authority.delayUntil == delayUntil) { - return; - } - authority.delayUntil = delayUntil; - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public long getDelayUntilTime(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getDelayUntil"); - if (authority == null) { - return 0; - } - return authority.delayUntil; - } - } - - private void updateOrRemovePeriodicSync(Account account, int userId, String providerName, - Bundle extras, - long period, boolean add) { - if (period <= 0) { - period = 0; - } - if (extras == null) { - extras = new Bundle(); - } - if (DEBUG) { - Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId - + ", provider " + providerName - + " -> period " + period + ", extras " + extras); - } - synchronized (mAuthorities) { - try { - AuthorityInfo authority = - getOrCreateAuthorityLocked(account, userId, providerName, -1, false); - if (add) { - // add this periodic sync if one with the same extras doesn't already - // exist in the periodicSyncs array - boolean alreadyPresent = false; - for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) { - Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i); - final Bundle existingExtras = syncInfo.first; - if (equals(existingExtras, extras)) { - if (syncInfo.second == period) { - return; - } - authority.periodicSyncs.set(i, Pair.create(extras, period)); - alreadyPresent = true; - break; - } - } - // if we added an entry to the periodicSyncs array also add an entry to - // the periodic syncs status to correspond to it - if (!alreadyPresent) { - authority.periodicSyncs.add(Pair.create(extras, period)); - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0); - } - } else { - // remove any periodic syncs that match the authority and extras - SyncStatusInfo status = mSyncStatus.get(authority.ident); - boolean changed = false; - Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator(); - int i = 0; - while (iterator.hasNext()) { - Pair<Bundle, Long> syncInfo = iterator.next(); - if (equals(syncInfo.first, extras)) { - iterator.remove(); - changed = true; - // if we removed an entry from the periodicSyncs array also - // remove the corresponding entry from the status - if (status != null) { - status.removePeriodicSyncTime(i); - } - } else { - i++; - } - } - if (!changed) { - return; - } - } - } finally { - writeAccountInfoLocked(); - writeStatusLocked(); - } - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras, - long pollFrequency) { - updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency, - true /* add */); - } - - public void removePeriodicSync(Account account, int userId, String providerName, - Bundle extras) { - updateOrRemovePeriodicSync(account, userId, providerName, extras, 0 /* period, ignored */, - false /* remove */); - } - - public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) { - ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>(); - synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getPeriodicSyncs"); - if (authority != null) { - for (Pair<Bundle, Long> item : authority.periodicSyncs) { - syncs.add(new PeriodicSync(account, providerName, item.first, - item.second)); - } - } - } - return syncs; - } - - public void setMasterSyncAutomatically(boolean flag, int userId) { - synchronized (mAuthorities) { - Boolean auto = mMasterSyncAutomatically.get(userId); - if (auto != null && (boolean) auto == flag) { - return; - } - mMasterSyncAutomatically.put(userId, flag); - writeAccountInfoLocked(); - } - if (flag) { - requestSync(null, userId, null, new Bundle()); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT); - } - - public boolean getMasterSyncAutomatically(int userId) { - synchronized (mAuthorities) { - Boolean auto = mMasterSyncAutomatically.get(userId); - return auto == null ? mDefaultMasterSyncAutomatically : auto; - } - } - - public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) { - synchronized (mAuthorities) { - return getOrCreateAuthorityLocked(account, userId, authority, - -1 /* assign a new identifier if creating a new authority */, - true /* write to storage if this results in a change */); - } - } - - public void removeAuthority(Account account, int userId, String authority) { - synchronized (mAuthorities) { - removeAuthorityLocked(account, userId, authority, true /* doWrite */); - } - } - - public AuthorityInfo getAuthority(int authorityId) { - synchronized (mAuthorities) { - return mAuthorities.get(authorityId); - } - } - - /** - * Returns true if there is currently a sync operation for the given - * account or authority actively being processed. - */ - public boolean isSyncActive(Account account, int userId, String authority) { - synchronized (mAuthorities) { - for (SyncInfo syncInfo : getCurrentSyncs(userId)) { - AuthorityInfo ainfo = getAuthority(syncInfo.authorityId); - if (ainfo != null && ainfo.account.equals(account) - && ainfo.authority.equals(authority) - && ainfo.userId == userId) { - return true; - } - } - } - - return false; - } - - public PendingOperation insertIntoPending(PendingOperation op) { - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "insertIntoPending: account=" + op.account - + " user=" + op.userId - + " auth=" + op.authority - + " src=" + op.syncSource - + " extras=" + op.extras); - } - - AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId, - op.authority, - -1 /* desired identifier */, - true /* write accounts to storage */); - if (authority == null) { - return null; - } - - op = new PendingOperation(op); - op.authorityId = authority.ident; - mPendingOperations.add(op); - appendPendingOperationLocked(op); - - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.pending = true; - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return op; - } - - public boolean deleteFromPending(PendingOperation op) { - boolean res = false; - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "deleteFromPending: account=" + op.account - + " user=" + op.userId - + " auth=" + op.authority - + " src=" + op.syncSource - + " extras=" + op.extras); - } - if (mPendingOperations.remove(op)) { - if (mPendingOperations.size() == 0 - || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) { - writePendingOperationsLocked(); - mNumPendingFinished = 0; - } else { - mNumPendingFinished++; - } - - AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority, - "deleteFromPending"); - if (authority != null) { - if (DEBUG) Log.v(TAG, "removing - " + authority); - final int N = mPendingOperations.size(); - boolean morePending = false; - for (int i=0; i<N; i++) { - PendingOperation cur = mPendingOperations.get(i); - if (cur.account.equals(op.account) - && cur.authority.equals(op.authority) - && cur.userId == op.userId) { - morePending = true; - break; - } - } - - if (!morePending) { - if (DEBUG) Log.v(TAG, "no more pending!"); - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.pending = false; - } - } - - res = true; - } - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return res; - } - - /** - * Return a copy of the current array of pending operations. The - * PendingOperation objects are the real objects stored inside, so that - * they can be used with deleteFromPending(). - */ - public ArrayList<PendingOperation> getPendingOperations() { - synchronized (mAuthorities) { - return new ArrayList<PendingOperation>(mPendingOperations); - } - } - - /** - * Return the number of currently pending operations. - */ - public int getPendingOperationCount() { - synchronized (mAuthorities) { - return mPendingOperations.size(); - } - } - - /** - * Called when the set of account has changed, given the new array of - * active accounts. - */ - public void doDatabaseCleanup(Account[] accounts, int userId) { - synchronized (mAuthorities) { - if (DEBUG) Log.v(TAG, "Updating for new accounts..."); - SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>(); - Iterator<AccountInfo> accIt = mAccounts.values().iterator(); - while (accIt.hasNext()) { - AccountInfo acc = accIt.next(); - if (!ArrayUtils.contains(accounts, acc.accountAndUser.account) - && acc.accountAndUser.userId == userId) { - // This account no longer exists... - if (DEBUG) { - Log.v(TAG, "Account removed: " + acc.accountAndUser); - } - for (AuthorityInfo auth : acc.authorities.values()) { - removing.put(auth.ident, auth); - } - accIt.remove(); - } - } - - // Clean out all data structures. - int i = removing.size(); - if (i > 0) { - while (i > 0) { - i--; - int ident = removing.keyAt(i); - mAuthorities.remove(ident); - int j = mSyncStatus.size(); - while (j > 0) { - j--; - if (mSyncStatus.keyAt(j) == ident) { - mSyncStatus.remove(mSyncStatus.keyAt(j)); - } - } - j = mSyncHistory.size(); - while (j > 0) { - j--; - if (mSyncHistory.get(j).authorityId == ident) { - mSyncHistory.remove(j); - } - } - } - writeAccountInfoLocked(); - writeStatusLocked(); - writePendingOperationsLocked(); - writeStatisticsLocked(); - } - } - } - - /** - * Called when a sync is starting. Supply a valid ActiveSyncContext with information - * about the sync. - */ - public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) { - final SyncInfo syncInfo; - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "setActiveSync: account=" - + activeSyncContext.mSyncOperation.account - + " auth=" + activeSyncContext.mSyncOperation.authority - + " src=" + activeSyncContext.mSyncOperation.syncSource - + " extras=" + activeSyncContext.mSyncOperation.extras); - } - AuthorityInfo authority = getOrCreateAuthorityLocked( - activeSyncContext.mSyncOperation.account, - activeSyncContext.mSyncOperation.userId, - activeSyncContext.mSyncOperation.authority, - -1 /* assign a new identifier if creating a new authority */, - true /* write to storage if this results in a change */); - syncInfo = new SyncInfo(authority.ident, - authority.account, authority.authority, - activeSyncContext.mStartTime); - getCurrentSyncs(authority.userId).add(syncInfo); - } - - reportActiveChange(); - return syncInfo; - } - - /** - * Called to indicate that a previously active sync is no longer active. - */ - public void removeActiveSync(SyncInfo syncInfo, int userId) { - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "removeActiveSync: account=" + syncInfo.account - + " user=" + userId - + " auth=" + syncInfo.authority); - } - getCurrentSyncs(userId).remove(syncInfo); - } - - reportActiveChange(); - } - - /** - * To allow others to send active change reports, to poke clients. - */ - public void reportActiveChange() { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE); - } - - /** - * Note that sync has started for the given account and authority. - */ - public long insertStartSyncEvent(Account accountName, int userId, String authorityName, - long now, int source, boolean initialization) { - long id; - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId - + " auth=" + authorityName + " source=" + source); - } - AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName, - "insertStartSyncEvent"); - if (authority == null) { - return -1; - } - SyncHistoryItem item = new SyncHistoryItem(); - item.initialization = initialization; - item.authorityId = authority.ident; - item.historyId = mNextHistoryId++; - if (mNextHistoryId < 0) mNextHistoryId = 0; - item.eventTime = now; - item.source = source; - item.event = EVENT_START; - mSyncHistory.add(0, item); - while (mSyncHistory.size() > MAX_HISTORY) { - mSyncHistory.remove(mSyncHistory.size()-1); - } - id = item.historyId; - if (DEBUG) Log.v(TAG, "returning historyId " + id); - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); - return id; - } - - public static boolean equals(Bundle b1, Bundle b2) { - if (b1.size() != b2.size()) { - return false; - } - if (b1.isEmpty()) { - return true; - } - for (String key : b1.keySet()) { - if (!b2.containsKey(key)) { - return false; - } - if (!b1.get(key).equals(b2.get(key))) { - return false; - } - } - return true; - } - - public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, - long downstreamActivity, long upstreamActivity) { - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "stopSyncEvent: historyId=" + historyId); - } - SyncHistoryItem item = null; - int i = mSyncHistory.size(); - while (i > 0) { - i--; - item = mSyncHistory.get(i); - if (item.historyId == historyId) { - break; - } - item = null; - } - - if (item == null) { - Log.w(TAG, "stopSyncEvent: no history for id " + historyId); - return; - } - - item.elapsedTime = elapsedTime; - item.event = EVENT_STOP; - item.mesg = resultMessage; - item.downstreamActivity = downstreamActivity; - item.upstreamActivity = upstreamActivity; - - SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId); - - status.numSyncs++; - status.totalElapsedTime += elapsedTime; - switch (item.source) { - case SOURCE_LOCAL: - status.numSourceLocal++; - break; - case SOURCE_POLL: - status.numSourcePoll++; - break; - case SOURCE_USER: - status.numSourceUser++; - break; - case SOURCE_SERVER: - status.numSourceServer++; - break; - case SOURCE_PERIODIC: - status.numSourcePeriodic++; - break; - } - - boolean writeStatisticsNow = false; - int day = getCurrentDayLocked(); - if (mDayStats[0] == null) { - mDayStats[0] = new DayStats(day); - } else if (day != mDayStats[0].day) { - System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1); - mDayStats[0] = new DayStats(day); - writeStatisticsNow = true; - } else if (mDayStats[0] == null) { - } - final DayStats ds = mDayStats[0]; - - final long lastSyncTime = (item.eventTime + elapsedTime); - boolean writeStatusNow = false; - if (MESG_SUCCESS.equals(resultMessage)) { - // - if successful, update the successful columns - if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) { - writeStatusNow = true; - } - status.lastSuccessTime = lastSyncTime; - status.lastSuccessSource = item.source; - status.lastFailureTime = 0; - status.lastFailureSource = -1; - status.lastFailureMesg = null; - status.initialFailureTime = 0; - ds.successCount++; - ds.successTime += elapsedTime; - } else if (!MESG_CANCELED.equals(resultMessage)) { - if (status.lastFailureTime == 0) { - writeStatusNow = true; - } - status.lastFailureTime = lastSyncTime; - status.lastFailureSource = item.source; - status.lastFailureMesg = resultMessage; - if (status.initialFailureTime == 0) { - status.initialFailureTime = lastSyncTime; - } - ds.failureCount++; - ds.failureTime += elapsedTime; - } - - if (writeStatusNow) { - writeStatusLocked(); - } else if (!hasMessages(MSG_WRITE_STATUS)) { - sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS), - WRITE_STATUS_DELAY); - } - if (writeStatisticsNow) { - writeStatisticsLocked(); - } else if (!hasMessages(MSG_WRITE_STATISTICS)) { - sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS), - WRITE_STATISTICS_DELAY); - } - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); - } - - /** - * Return a list of the currently active syncs. Note that the returned items are the - * real, live active sync objects, so be careful what you do with it. - */ - public List<SyncInfo> getCurrentSyncs(int userId) { - synchronized (mAuthorities) { - ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId); - if (syncs == null) { - syncs = new ArrayList<SyncInfo>(); - mCurrentSyncs.put(userId, syncs); - } - return syncs; - } - } - - /** - * Return an array of the current sync status for all authorities. Note - * that the objects inside the array are the real, live status objects, - * so be careful what you do with them. - */ - public ArrayList<SyncStatusInfo> getSyncStatus() { - synchronized (mAuthorities) { - final int N = mSyncStatus.size(); - ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N); - for (int i=0; i<N; i++) { - ops.add(mSyncStatus.valueAt(i)); - } - return ops; - } - } - - /** - * Return an array of the current authorities. Note - * that the objects inside the array are the real, live objects, - * so be careful what you do with them. - */ - public ArrayList<AuthorityInfo> getAuthorities() { - synchronized (mAuthorities) { - final int N = mAuthorities.size(); - ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N); - for (int i=0; i<N; i++) { - // Make deep copy because AuthorityInfo syncs are liable to change. - infos.add(new AuthorityInfo(mAuthorities.valueAt(i))); - } - return infos; - } - } - - /** - * Returns the status that matches the authority and account. - * - * @param account the account we want to check - * @param authority the authority whose row should be selected - * @return the SyncStatusInfo for the authority - */ - public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId, - String authority) { - if (account == null || authority == null) { - throw new IllegalArgumentException(); - } - synchronized (mAuthorities) { - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - SyncStatusInfo cur = mSyncStatus.valueAt(i); - AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); - - if (ainfo != null && ainfo.authority.equals(authority) - && ainfo.userId == userId - && account.equals(ainfo.account)) { - return cur; - } - } - return null; - } - } - - /** - * Return true if the pending status is true of any matching authorities. - */ - public boolean isSyncPending(Account account, int userId, String authority) { - synchronized (mAuthorities) { - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - SyncStatusInfo cur = mSyncStatus.valueAt(i); - AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); - if (ainfo == null) { - continue; - } - if (userId != ainfo.userId) { - continue; - } - if (account != null && !ainfo.account.equals(account)) { - continue; - } - if (ainfo.authority.equals(authority) && cur.pending) { - return true; - } - } - return false; - } - } - - /** - * Return an array of the current sync status for all authorities. Note - * that the objects inside the array are the real, live status objects, - * so be careful what you do with them. - */ - public ArrayList<SyncHistoryItem> getSyncHistory() { - synchronized (mAuthorities) { - final int N = mSyncHistory.size(); - ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N); - for (int i=0; i<N; i++) { - items.add(mSyncHistory.get(i)); - } - return items; - } - } - - /** - * Return an array of the current per-day statistics. Note - * that the objects inside the array are the real, live status objects, - * so be careful what you do with them. - */ - public DayStats[] getDayStatistics() { - synchronized (mAuthorities) { - DayStats[] ds = new DayStats[mDayStats.length]; - System.arraycopy(mDayStats, 0, ds, 0, ds.length); - return ds; - } - } - - private int getCurrentDayLocked() { - mCal.setTimeInMillis(System.currentTimeMillis()); - final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR); - if (mYear != mCal.get(Calendar.YEAR)) { - mYear = mCal.get(Calendar.YEAR); - mCal.clear(); - mCal.set(Calendar.YEAR, mYear); - mYearInDays = (int)(mCal.getTimeInMillis()/86400000); - } - return dayOfYear + mYearInDays; - } - - /** - * Retrieve an authority, returning null if one does not exist. - * - * @param accountName The name of the account for the authority. - * @param authorityName The name of the authority itself. - * @param tag If non-null, this will be used in a log message if the - * requested authority does not exist. - */ - private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName, - String tag) { - AccountAndUser au = new AccountAndUser(accountName, userId); - AccountInfo accountInfo = mAccounts.get(au); - if (accountInfo == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown account " + au); - } - } - return null; - } - AuthorityInfo authority = accountInfo.authorities.get(authorityName); - if (authority == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown authority " + authorityName); - } - } - return null; - } - - return authority; - } - - private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId, - String authorityName, int ident, boolean doWrite) { - AccountAndUser au = new AccountAndUser(accountName, userId); - AccountInfo account = mAccounts.get(au); - if (account == null) { - account = new AccountInfo(au); - mAccounts.put(au, account); - } - AuthorityInfo authority = account.authorities.get(authorityName); - if (authority == null) { - if (ident < 0) { - ident = mNextAuthorityId; - mNextAuthorityId++; - doWrite = true; - } - if (DEBUG) { - Log.v(TAG, "created a new AuthorityInfo for " + accountName - + ", user " + userId - + ", provider " + authorityName); - } - authority = new AuthorityInfo(accountName, userId, authorityName, ident); - account.authorities.put(authorityName, authority); - mAuthorities.put(ident, authority); - if (doWrite) { - writeAccountInfoLocked(); - } - } - - return authority; - } - - private void removeAuthorityLocked(Account account, int userId, String authorityName, - boolean doWrite) { - AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId)); - if (accountInfo != null) { - final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName); - if (authorityInfo != null) { - mAuthorities.remove(authorityInfo.ident); - if (doWrite) { - writeAccountInfoLocked(); - } - } - } - } - - public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) { - synchronized (mAuthorities) { - return getOrCreateSyncStatusLocked(authority.ident); - } - } - - private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) { - SyncStatusInfo status = mSyncStatus.get(authorityId); - if (status == null) { - status = new SyncStatusInfo(authorityId); - mSyncStatus.put(authorityId, status); - } - return status; - } - - public void writeAllState() { - synchronized (mAuthorities) { - // Account info is always written so no need to do it here. - - if (mNumPendingFinished > 0) { - // Only write these if they are out of date. - writePendingOperationsLocked(); - } - - // Just always write these... they are likely out of date. - writeStatusLocked(); - writeStatisticsLocked(); - } - } - - /** - * public for testing - */ - public void clearAndReadState() { - synchronized (mAuthorities) { - mAuthorities.clear(); - mAccounts.clear(); - mPendingOperations.clear(); - mSyncStatus.clear(); - mSyncHistory.clear(); - - readAccountInfoLocked(); - readStatusLocked(); - readPendingOperationsLocked(); - readStatisticsLocked(); - readAndDeleteLegacyAccountInfoLocked(); - writeAccountInfoLocked(); - writeStatusLocked(); - writePendingOperationsLocked(); - writeStatisticsLocked(); - } - } - - /** - * Read all account information back in to the initial engine state. - */ - private void readAccountInfoLocked() { - int highestAuthorityId = -1; - FileInputStream fis = null; - try { - fis = mAccountInfoFile.openRead(); - if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile()); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG) { - eventType = parser.next(); - } - String tagName = parser.getName(); - if ("accounts".equals(tagName)) { - String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES); - String versionString = parser.getAttributeValue(null, "version"); - int version; - try { - version = (versionString == null) ? 0 : Integer.parseInt(versionString); - } catch (NumberFormatException e) { - version = 0; - } - String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID); - try { - int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString); - mNextAuthorityId = Math.max(mNextAuthorityId, id); - } catch (NumberFormatException e) { - // don't care - } - String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET); - try { - mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString); - } catch (NumberFormatException e) { - mSyncRandomOffset = 0; - } - if (mSyncRandomOffset == 0) { - Random random = new Random(System.currentTimeMillis()); - mSyncRandomOffset = random.nextInt(86400); - } - mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen)); - eventType = parser.next(); - AuthorityInfo authority = null; - Pair<Bundle, Long> periodicSync = null; - do { - if (eventType == XmlPullParser.START_TAG) { - tagName = parser.getName(); - if (parser.getDepth() == 2) { - if ("authority".equals(tagName)) { - authority = parseAuthority(parser, version); - periodicSync = null; - if (authority.ident > highestAuthorityId) { - highestAuthorityId = authority.ident; - } - } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) { - parseListenForTickles(parser); - } - } else if (parser.getDepth() == 3) { - if ("periodicSync".equals(tagName) && authority != null) { - periodicSync = parsePeriodicSync(parser, authority); - } - } else if (parser.getDepth() == 4 && periodicSync != null) { - if ("extra".equals(tagName)) { - parseExtra(parser, periodicSync); - } - } - } - eventType = parser.next(); - } while (eventType != XmlPullParser.END_DOCUMENT); - } - } catch (XmlPullParserException e) { - Log.w(TAG, "Error reading accounts", e); - return; - } catch (java.io.IOException e) { - if (fis == null) Log.i(TAG, "No initial accounts"); - else Log.w(TAG, "Error reading accounts", e); - return; - } finally { - mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId); - if (fis != null) { - try { - fis.close(); - } catch (java.io.IOException e1) { - } - } - } - - maybeMigrateSettingsForRenamedAuthorities(); - } - - /** - * some authority names have changed. copy over their settings and delete the old ones - * @return true if a change was made - */ - private boolean maybeMigrateSettingsForRenamedAuthorities() { - boolean writeNeeded = false; - - ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>(); - final int N = mAuthorities.size(); - for (int i=0; i<N; i++) { - AuthorityInfo authority = mAuthorities.valueAt(i); - // skip this authority if it isn't one of the renamed ones - final String newAuthorityName = sAuthorityRenames.get(authority.authority); - if (newAuthorityName == null) { - continue; - } - - // remember this authority so we can remove it later. we can't remove it - // now without messing up this loop iteration - authoritiesToRemove.add(authority); - - // this authority isn't enabled, no need to copy it to the new authority name since - // the default is "disabled" - if (!authority.enabled) { - continue; - } - - // if we already have a record of this new authority then don't copy over the settings - if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup") - != null) { - continue; - } - - AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account, - authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */); - newAuthority.enabled = true; - writeNeeded = true; - } - - for (AuthorityInfo authorityInfo : authoritiesToRemove) { - removeAuthorityLocked(authorityInfo.account, authorityInfo.userId, - authorityInfo.authority, false /* doWrite */); - writeNeeded = true; - } - - return writeNeeded; - } - - private void parseListenForTickles(XmlPullParser parser) { - String user = parser.getAttributeValue(null, XML_ATTR_USER); - int userId = 0; - try { - userId = Integer.parseInt(user); - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing the user for listen-for-tickles", e); - } catch (NullPointerException e) { - Log.e(TAG, "the user in listen-for-tickles is null", e); - } - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); - boolean listen = enabled == null || Boolean.parseBoolean(enabled); - mMasterSyncAutomatically.put(userId, listen); - } - - private AuthorityInfo parseAuthority(XmlPullParser parser, int version) { - AuthorityInfo authority = null; - int id = -1; - try { - id = Integer.parseInt(parser.getAttributeValue( - null, "id")); - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing the id of the authority", e); - } catch (NullPointerException e) { - Log.e(TAG, "the id of the authority is null", e); - } - if (id >= 0) { - String authorityName = parser.getAttributeValue(null, "authority"); - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); - String syncable = parser.getAttributeValue(null, "syncable"); - String accountName = parser.getAttributeValue(null, "account"); - String accountType = parser.getAttributeValue(null, "type"); - String user = parser.getAttributeValue(null, XML_ATTR_USER); - int userId = user == null ? 0 : Integer.parseInt(user); - if (accountType == null) { - accountType = "com.google"; - syncable = "unknown"; - } - authority = mAuthorities.get(id); - if (DEBUG_FILE) Log.v(TAG, "Adding authority: account=" - + accountName + " auth=" + authorityName - + " user=" + userId - + " enabled=" + enabled - + " syncable=" + syncable); - if (authority == null) { - if (DEBUG_FILE) Log.v(TAG, "Creating entry"); - authority = getOrCreateAuthorityLocked( - new Account(accountName, accountType), userId, authorityName, id, false); - // If the version is 0 then we are upgrading from a file format that did not - // know about periodic syncs. In that case don't clear the list since we - // want the default, which is a daily periodioc sync. - // Otherwise clear out this default list since we will populate it later with - // the periodic sync descriptions that are read from the configuration file. - if (version > 0) { - authority.periodicSyncs.clear(); - } - } - if (authority != null) { - authority.enabled = enabled == null || Boolean.parseBoolean(enabled); - if ("unknown".equals(syncable)) { - authority.syncable = -1; - } else { - authority.syncable = - (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0; - } - } else { - Log.w(TAG, "Failure adding authority: account=" - + accountName + " auth=" + authorityName - + " enabled=" + enabled - + " syncable=" + syncable); - } - } - - return authority; - } - - private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) { - Bundle extras = new Bundle(); - String periodValue = parser.getAttributeValue(null, "period"); - final long period; - try { - period = Long.parseLong(periodValue); - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing the period of a periodic sync", e); - return null; - } catch (NullPointerException e) { - Log.e(TAG, "the period of a periodic sync is null", e); - return null; - } - final Pair<Bundle, Long> periodicSync = Pair.create(extras, period); - authority.periodicSyncs.add(periodicSync); - - return periodicSync; - } - - private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) { - final Bundle extras = periodicSync.first; - String name = parser.getAttributeValue(null, "name"); - String type = parser.getAttributeValue(null, "type"); - String value1 = parser.getAttributeValue(null, "value1"); - String value2 = parser.getAttributeValue(null, "value2"); - - try { - if ("long".equals(type)) { - extras.putLong(name, Long.parseLong(value1)); - } else if ("integer".equals(type)) { - extras.putInt(name, Integer.parseInt(value1)); - } else if ("double".equals(type)) { - extras.putDouble(name, Double.parseDouble(value1)); - } else if ("float".equals(type)) { - extras.putFloat(name, Float.parseFloat(value1)); - } else if ("boolean".equals(type)) { - extras.putBoolean(name, Boolean.parseBoolean(value1)); - } else if ("string".equals(type)) { - extras.putString(name, value1); - } else if ("account".equals(type)) { - extras.putParcelable(name, new Account(value1, value2)); - } - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing bundle value", e); - } catch (NullPointerException e) { - Log.e(TAG, "error parsing bundle value", e); - } - } - - /** - * Write all account information to the account file. - */ - private void writeAccountInfoLocked() { - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile()); - FileOutputStream fos = null; - - try { - fos = mAccountInfoFile.startWrite(); - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, "utf-8"); - out.startDocument(null, true); - out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - out.startTag(null, "accounts"); - out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION)); - out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId)); - out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset)); - - // Write the Sync Automatically flags for each user - final int M = mMasterSyncAutomatically.size(); - for (int m = 0; m < M; m++) { - int userId = mMasterSyncAutomatically.keyAt(m); - Boolean listen = mMasterSyncAutomatically.valueAt(m); - out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES); - out.attribute(null, XML_ATTR_USER, Integer.toString(userId)); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen)); - out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES); - } - - final int N = mAuthorities.size(); - for (int i=0; i<N; i++) { - AuthorityInfo authority = mAuthorities.valueAt(i); - out.startTag(null, "authority"); - out.attribute(null, "id", Integer.toString(authority.ident)); - out.attribute(null, "account", authority.account.name); - out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId)); - out.attribute(null, "type", authority.account.type); - out.attribute(null, "authority", authority.authority); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled)); - if (authority.syncable < 0) { - out.attribute(null, "syncable", "unknown"); - } else { - out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0)); - } - for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) { - out.startTag(null, "periodicSync"); - out.attribute(null, "period", Long.toString(periodicSync.second)); - final Bundle extras = periodicSync.first; - for (String key : extras.keySet()) { - out.startTag(null, "extra"); - out.attribute(null, "name", key); - final Object value = extras.get(key); - if (value instanceof Long) { - out.attribute(null, "type", "long"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Integer) { - out.attribute(null, "type", "integer"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Boolean) { - out.attribute(null, "type", "boolean"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Float) { - out.attribute(null, "type", "float"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Double) { - out.attribute(null, "type", "double"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof String) { - out.attribute(null, "type", "string"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Account) { - out.attribute(null, "type", "account"); - out.attribute(null, "value1", ((Account)value).name); - out.attribute(null, "value2", ((Account)value).type); - } - out.endTag(null, "extra"); - } - out.endTag(null, "periodicSync"); - } - out.endTag(null, "authority"); - } - - out.endTag(null, "accounts"); - - out.endDocument(); - - mAccountInfoFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing accounts", e1); - if (fos != null) { - mAccountInfoFile.failWrite(fos); - } - } - } - - static int getIntColumn(Cursor c, String name) { - return c.getInt(c.getColumnIndex(name)); - } - - static long getLongColumn(Cursor c, String name) { - return c.getLong(c.getColumnIndex(name)); - } - - /** - * Load sync engine state from the old syncmanager database, and then - * erase it. Note that we don't deal with pending operations, active - * sync, or history. - */ - private void readAndDeleteLegacyAccountInfoLocked() { - // Look for old database to initialize from. - File file = mContext.getDatabasePath("syncmanager.db"); - if (!file.exists()) { - return; - } - String path = file.getPath(); - SQLiteDatabase db = null; - try { - db = SQLiteDatabase.openDatabase(path, null, - SQLiteDatabase.OPEN_READONLY); - } catch (SQLiteException e) { - } - - if (db != null) { - final boolean hasType = db.getVersion() >= 11; - - // Copy in all of the status information, as well as accounts. - if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db"); - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables("stats, status"); - HashMap<String,String> map = new HashMap<String,String>(); - map.put("_id", "status._id as _id"); - map.put("account", "stats.account as account"); - if (hasType) { - map.put("account_type", "stats.account_type as account_type"); - } - map.put("authority", "stats.authority as authority"); - map.put("totalElapsedTime", "totalElapsedTime"); - map.put("numSyncs", "numSyncs"); - map.put("numSourceLocal", "numSourceLocal"); - map.put("numSourcePoll", "numSourcePoll"); - map.put("numSourceServer", "numSourceServer"); - map.put("numSourceUser", "numSourceUser"); - map.put("lastSuccessSource", "lastSuccessSource"); - map.put("lastSuccessTime", "lastSuccessTime"); - map.put("lastFailureSource", "lastFailureSource"); - map.put("lastFailureTime", "lastFailureTime"); - map.put("lastFailureMesg", "lastFailureMesg"); - map.put("pending", "pending"); - qb.setProjectionMap(map); - qb.appendWhere("stats._id = status.stats_id"); - Cursor c = qb.query(db, null, null, null, null, null, null); - while (c.moveToNext()) { - String accountName = c.getString(c.getColumnIndex("account")); - String accountType = hasType - ? c.getString(c.getColumnIndex("account_type")) : null; - if (accountType == null) { - accountType = "com.google"; - } - String authorityName = c.getString(c.getColumnIndex("authority")); - AuthorityInfo authority = this.getOrCreateAuthorityLocked( - new Account(accountName, accountType), 0 /* legacy is single-user */, - authorityName, -1, false); - if (authority != null) { - int i = mSyncStatus.size(); - boolean found = false; - SyncStatusInfo st = null; - while (i > 0) { - i--; - st = mSyncStatus.valueAt(i); - if (st.authorityId == authority.ident) { - found = true; - break; - } - } - if (!found) { - st = new SyncStatusInfo(authority.ident); - mSyncStatus.put(authority.ident, st); - } - st.totalElapsedTime = getLongColumn(c, "totalElapsedTime"); - st.numSyncs = getIntColumn(c, "numSyncs"); - st.numSourceLocal = getIntColumn(c, "numSourceLocal"); - st.numSourcePoll = getIntColumn(c, "numSourcePoll"); - st.numSourceServer = getIntColumn(c, "numSourceServer"); - st.numSourceUser = getIntColumn(c, "numSourceUser"); - st.numSourcePeriodic = 0; - st.lastSuccessSource = getIntColumn(c, "lastSuccessSource"); - st.lastSuccessTime = getLongColumn(c, "lastSuccessTime"); - st.lastFailureSource = getIntColumn(c, "lastFailureSource"); - st.lastFailureTime = getLongColumn(c, "lastFailureTime"); - st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg")); - st.pending = getIntColumn(c, "pending") != 0; - } - } - - c.close(); - - // Retrieve the settings. - qb = new SQLiteQueryBuilder(); - qb.setTables("settings"); - c = qb.query(db, null, null, null, null, null, null); - while (c.moveToNext()) { - String name = c.getString(c.getColumnIndex("name")); - String value = c.getString(c.getColumnIndex("value")); - if (name == null) continue; - if (name.equals("listen_for_tickles")) { - setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0); - } else if (name.startsWith("sync_provider_")) { - String provider = name.substring("sync_provider_".length(), - name.length()); - int i = mAuthorities.size(); - while (i > 0) { - i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(provider)) { - authority.enabled = value == null || Boolean.parseBoolean(value); - authority.syncable = 1; - } - } - } - } - - c.close(); - - db.close(); - - (new File(path)).delete(); - } - } - - public static final int STATUS_FILE_END = 0; - public static final int STATUS_FILE_ITEM = 100; - - /** - * Read all sync status back in to the initial engine state. - */ - private void readStatusLocked() { - if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile()); - try { - byte[] data = mStatusFile.readFully(); - Parcel in = Parcel.obtain(); - in.unmarshall(data, 0, data.length); - in.setDataPosition(0); - int token; - while ((token=in.readInt()) != STATUS_FILE_END) { - if (token == STATUS_FILE_ITEM) { - SyncStatusInfo status = new SyncStatusInfo(in); - if (mAuthorities.indexOfKey(status.authorityId) >= 0) { - status.pending = false; - if (DEBUG_FILE) Log.v(TAG, "Adding status for id " - + status.authorityId); - mSyncStatus.put(status.authorityId, status); - } - } else { - // Ooops. - Log.w(TAG, "Unknown status token: " + token); - break; - } - } - } catch (java.io.IOException e) { - Log.i(TAG, "No initial status"); - } - } - - /** - * Write all sync status to the sync status file. - */ - private void writeStatusLocked() { - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile()); - - // The file is being written, so we don't need to have a scheduled - // write until the next change. - removeMessages(MSG_WRITE_STATUS); - - FileOutputStream fos = null; - try { - fos = mStatusFile.startWrite(); - Parcel out = Parcel.obtain(); - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - SyncStatusInfo status = mSyncStatus.valueAt(i); - out.writeInt(STATUS_FILE_ITEM); - status.writeToParcel(out, 0); - } - out.writeInt(STATUS_FILE_END); - fos.write(out.marshall()); - out.recycle(); - - mStatusFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing status", e1); - if (fos != null) { - mStatusFile.failWrite(fos); - } - } - } - - public static final int PENDING_OPERATION_VERSION = 2; - - /** - * Read all pending operations back in to the initial engine state. - */ - private void readPendingOperationsLocked() { - if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile()); - try { - byte[] data = mPendingFile.readFully(); - Parcel in = Parcel.obtain(); - in.unmarshall(data, 0, data.length); - in.setDataPosition(0); - final int SIZE = in.dataSize(); - while (in.dataPosition() < SIZE) { - int version = in.readInt(); - if (version != PENDING_OPERATION_VERSION && version != 1) { - Log.w(TAG, "Unknown pending operation version " - + version + "; dropping all ops"); - break; - } - int authorityId = in.readInt(); - int syncSource = in.readInt(); - byte[] flatExtras = in.createByteArray(); - boolean expedited; - if (version == PENDING_OPERATION_VERSION) { - expedited = in.readInt() != 0; - } else { - expedited = false; - } - AuthorityInfo authority = mAuthorities.get(authorityId); - if (authority != null) { - Bundle extras; - if (flatExtras != null) { - extras = unflattenBundle(flatExtras); - } else { - // if we are unable to parse the extras for whatever reason convert this - // to a regular sync by creating an empty extras - extras = new Bundle(); - } - PendingOperation op = new PendingOperation( - authority.account, authority.userId, syncSource, - authority.authority, extras, expedited); - op.authorityId = authorityId; - op.flatExtras = flatExtras; - if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account - + " auth=" + op.authority - + " src=" + op.syncSource - + " expedited=" + op.expedited - + " extras=" + op.extras); - mPendingOperations.add(op); - } - } - } catch (java.io.IOException e) { - Log.i(TAG, "No initial pending operations"); - } - } - - private void writePendingOperationLocked(PendingOperation op, Parcel out) { - out.writeInt(PENDING_OPERATION_VERSION); - out.writeInt(op.authorityId); - out.writeInt(op.syncSource); - if (op.flatExtras == null && op.extras != null) { - op.flatExtras = flattenBundle(op.extras); - } - out.writeByteArray(op.flatExtras); - out.writeInt(op.expedited ? 1 : 0); - } - - /** - * Write all currently pending ops to the pending ops file. - */ - private void writePendingOperationsLocked() { - final int N = mPendingOperations.size(); - FileOutputStream fos = null; - try { - if (N == 0) { - if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile()); - mPendingFile.truncate(); - return; - } - - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile()); - fos = mPendingFile.startWrite(); - - Parcel out = Parcel.obtain(); - for (int i=0; i<N; i++) { - PendingOperation op = mPendingOperations.get(i); - writePendingOperationLocked(op, out); - } - fos.write(out.marshall()); - out.recycle(); - - mPendingFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing pending operations", e1); - if (fos != null) { - mPendingFile.failWrite(fos); - } - } - } - - /** - * Append the given operation to the pending ops file; if unable to, - * write all pending ops. - */ - private void appendPendingOperationLocked(PendingOperation op) { - if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile()); - FileOutputStream fos = null; - try { - fos = mPendingFile.openAppend(); - } catch (java.io.IOException e) { - if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file"); - writePendingOperationsLocked(); - return; - } - - try { - Parcel out = Parcel.obtain(); - writePendingOperationLocked(op, out); - fos.write(out.marshall()); - out.recycle(); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing pending operations", e1); - } finally { - try { - fos.close(); - } catch (java.io.IOException e2) { - } - } - } - - static private byte[] flattenBundle(Bundle bundle) { - byte[] flatData = null; - Parcel parcel = Parcel.obtain(); - try { - bundle.writeToParcel(parcel, 0); - flatData = parcel.marshall(); - } finally { - parcel.recycle(); - } - return flatData; - } - - static private Bundle unflattenBundle(byte[] flatData) { - Bundle bundle; - Parcel parcel = Parcel.obtain(); - try { - parcel.unmarshall(flatData, 0, flatData.length); - parcel.setDataPosition(0); - bundle = parcel.readBundle(); - } catch (RuntimeException e) { - // A RuntimeException is thrown if we were unable to parse the parcel. - // Create an empty parcel in this case. - bundle = new Bundle(); - } finally { - parcel.recycle(); - } - return bundle; - } - - private void requestSync(Account account, int userId, String authority, Bundle extras) { - // If this is happening in the system process, then call the syncrequest listener - // to make a request back to the SyncManager directly. - // If this is probably a test instance, then call back through the ContentResolver - // which will know which userId to apply based on the Binder id. - if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID - && mSyncRequestListener != null) { - mSyncRequestListener.onSyncRequest(account, userId, authority, extras); - } else { - ContentResolver.requestSync(account, authority, extras); - } - } - - public static final int STATISTICS_FILE_END = 0; - public static final int STATISTICS_FILE_ITEM_OLD = 100; - public static final int STATISTICS_FILE_ITEM = 101; - - /** - * Read all sync statistics back in to the initial engine state. - */ - private void readStatisticsLocked() { - try { - byte[] data = mStatisticsFile.readFully(); - Parcel in = Parcel.obtain(); - in.unmarshall(data, 0, data.length); - in.setDataPosition(0); - int token; - int index = 0; - while ((token=in.readInt()) != STATISTICS_FILE_END) { - if (token == STATISTICS_FILE_ITEM - || token == STATISTICS_FILE_ITEM_OLD) { - int day = in.readInt(); - if (token == STATISTICS_FILE_ITEM_OLD) { - day = day - 2009 + 14245; // Magic! - } - DayStats ds = new DayStats(day); - ds.successCount = in.readInt(); - ds.successTime = in.readLong(); - ds.failureCount = in.readInt(); - ds.failureTime = in.readLong(); - if (index < mDayStats.length) { - mDayStats[index] = ds; - index++; - } - } else { - // Ooops. - Log.w(TAG, "Unknown stats token: " + token); - break; - } - } - } catch (java.io.IOException e) { - Log.i(TAG, "No initial statistics"); - } - } - - /** - * Write all sync statistics to the sync status file. - */ - private void writeStatisticsLocked() { - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile()); - - // The file is being written, so we don't need to have a scheduled - // write until the next change. - removeMessages(MSG_WRITE_STATISTICS); - - FileOutputStream fos = null; - try { - fos = mStatisticsFile.startWrite(); - Parcel out = Parcel.obtain(); - final int N = mDayStats.length; - for (int i=0; i<N; i++) { - DayStats ds = mDayStats[i]; - if (ds == null) { - break; - } - out.writeInt(STATISTICS_FILE_ITEM); - out.writeInt(ds.day); - out.writeInt(ds.successCount); - out.writeLong(ds.successTime); - out.writeInt(ds.failureCount); - out.writeLong(ds.failureTime); - } - out.writeInt(STATISTICS_FILE_END); - fos.write(out.marshall()); - out.recycle(); - - mStatisticsFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing stats", e1); - if (fos != null) { - mStatisticsFile.failWrite(fos); - } - } - } -} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index b9e432c..a368451 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -127,7 +127,16 @@ interface IPackageManager { * limit that kicks in when flags are included that bloat up the data * returned. */ - ParceledListSlice getInstalledPackages(int flags, in String lastRead, in int userId); + ParceledListSlice getInstalledPackages(int flags, in int userId); + + /** + * This implements getPackagesHoldingPermissions via a "last returned row" + * mechanism that is not exposed in the API. This is to get around the IPC + * limit that kicks in when flags are included that bloat up the data + * returned. + */ + ParceledListSlice getPackagesHoldingPermissions(in String[] permissions, + int flags, int userId); /** * This implements getInstalledApplications via a "last returned row" @@ -135,7 +144,7 @@ interface IPackageManager { * limit that kicks in when flags are included that bloat up the data * returned. */ - ParceledListSlice getInstalledApplications(int flags, in String lastRead, int userId); + ParceledListSlice getInstalledApplications(int flags, int userId); /** * Retrieve all applications that are marked as persistent. @@ -201,6 +210,8 @@ interface IPackageManager { List<PackageInfo> getPreferredPackages(int flags); + void resetPreferredActivities(int userId); + void addPreferredActivity(in IntentFilter filter, int match, in ComponentName[] set, in ComponentName activity, int userId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 8ba1988..c507245 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -175,6 +175,14 @@ public abstract class PackageManager { public static final int GET_CONFIGURATIONS = 0x00004000; /** + * {@link PackageInfo} flag: include disabled components which are in + * that state only because of {@link #COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} + * in the returned info. Note that if you set this flag, applications + * that are in this disabled state will be reported as enabled. + */ + public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000; + + /** * Resolution and querying flag: if set, only filters that support the * {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for * matching. This is a synonym for including the CATEGORY_DEFAULT in your @@ -265,6 +273,19 @@ public abstract class PackageManager { public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; /** + * Flag for {@link #setApplicationEnabledSetting(String, int, int)} only: This + * application should be considered, until the point where the user actually + * wants to use it. This means that it will not normally show up to the user + * (such as in the launcher), but various parts of the user interface can + * use {@link #GET_DISABLED_UNTIL_USED_COMPONENTS} to still see it and allow + * the user to select it (as for example an IME, device admin, etc). Such code, + * once the user has selected the app, should at that point also make it enabled. + * This option currently <strong>can not</strong> be used with + * {@link #setComponentEnabledSetting(ComponentName, int, int)}. + */ + public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; + + /** * Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to * indicate that this package should be installed as forward locked, i.e. only the app itself * should have access to its code and non-resource assets. @@ -645,6 +666,15 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_INTERNAL_ERROR = -110; /** + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the package because the user is restricted from installing + * apps. + * @hide + */ + public static final int INSTALL_FAILED_USER_RESTRICTED = -111; + + /** * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the * package's data directory. * @@ -689,6 +719,15 @@ public abstract class PackageManager { public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2; /** + * Deletion failed return code: this is passed to the + * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system + * failed to delete the package since the user is restricted. + * + * @hide + */ + public static final int DELETE_FAILED_USER_RESTRICTED = -3; + + /** * Return code that is passed to the {@link IPackageMoveObserver} by * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} when the * package has been successfully moved by the system. @@ -1280,6 +1319,22 @@ public abstract class PackageManager { throws NameNotFoundException; /** + * @hide Return the uid associated with the given package name for the + * given user. + * + * <p>Throws {@link NameNotFoundException} if a package with the given + * name can not be found on the system. + * + * @param packageName The full name (i.e. com.google.apps.contacts) of the + * desired package. + * @param userHandle The user handle identifier to look up the package under. + * + * @return Returns an integer uid who owns the given package name. + */ + public abstract int getPackageUid(String packageName, int userHandle) + throws NameNotFoundException; + + /** * Retrieve all of the information we know about a particular permission. * * <p>Throws {@link NameNotFoundException} if a permission with the given @@ -1496,11 +1551,43 @@ public abstract class PackageManager { * @see #GET_SERVICES * @see #GET_SIGNATURES * @see #GET_UNINSTALLED_PACKAGES - * */ public abstract List<PackageInfo> getInstalledPackages(int flags); /** + * Return a List of all installed packages that are currently + * holding any of the given permissions. + * + * @param flags Additional option flags. Use any combination of + * {@link #GET_ACTIVITIES}, + * {@link #GET_GIDS}, + * {@link #GET_CONFIGURATIONS}, + * {@link #GET_INSTRUMENTATION}, + * {@link #GET_PERMISSIONS}, + * {@link #GET_PROVIDERS}, + * {@link #GET_RECEIVERS}, + * {@link #GET_SERVICES}, + * {@link #GET_SIGNATURES}, + * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned. + * + * @return Returns a List of PackageInfo objects, one for each installed + * application that is holding any of the permissions that were provided. + * + * @see #GET_ACTIVITIES + * @see #GET_GIDS + * @see #GET_CONFIGURATIONS + * @see #GET_INSTRUMENTATION + * @see #GET_PERMISSIONS + * @see #GET_PROVIDERS + * @see #GET_RECEIVERS + * @see #GET_SERVICES + * @see #GET_SIGNATURES + * @see #GET_UNINSTALLED_PACKAGES + */ + public abstract List<PackageInfo> getPackagesHoldingPermissions( + String[] permissions, int flags); + + /** * Return a List of all packages that are installed on the device, for a specific user. * Requesting a list of installed packages for another user * will require the permission INTERACT_ACROSS_USERS_FULL. @@ -1726,14 +1813,14 @@ public abstract class PackageManager { /** * Return a List of all application packages that are installed on the * device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all - * applications including those deleted with DONT_DELETE_DATA(partially + * applications including those deleted with DONT_DELETE_DATA (partially * installed apps with data directory) will be returned. * * @param flags Additional option flags. Use any combination of * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES}, * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned. * - * @return A List of ApplicationInfo objects, one for each application that + * @return Returns a List of ApplicationInfo objects, one for each application that * is installed on the device. In the unlikely case of there being * no installed applications, an empty list is returned. * If flag GET_UNINSTALLED_PACKAGES is set, a list of all diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 3e8c2a8..e1887bc 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3527,29 +3527,45 @@ public class PackageParser { return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId()); } + private static void updateApplicationInfo(ApplicationInfo ai, int flags, + PackageUserState state) { + // CompatibilityMode is global state. + if (!sCompatibilityModeEnabled) { + ai.disableCompatibilityMode(); + } + if (state.installed) { + ai.flags |= ApplicationInfo.FLAG_INSTALLED; + } else { + ai.flags &= ~ApplicationInfo.FLAG_INSTALLED; + } + if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + ai.enabled = true; + } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { + ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0; + } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED + || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { + ai.enabled = false; + } + ai.enabledSetting = state.enabled; + } + public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state, int userId) { if (p == null) return null; if (!checkUseInstalled(flags, state)) { return null; } - if (!copyNeeded(flags, p, state, null, userId)) { - // CompatibilityMode is global state. It's safe to modify the instance - // of the package. - if (!sCompatibilityModeEnabled) { - p.applicationInfo.disableCompatibilityMode(); - } - // Make sure we report as installed. Also safe to do, since the - // default state should be installed (we will always copy if we - // need to report it is not installed). - p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; - if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { - p.applicationInfo.enabled = true; - } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED - || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { - p.applicationInfo.enabled = false; - } - p.applicationInfo.enabledSetting = state.enabled; + if (!copyNeeded(flags, p, state, null, userId) + && ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0 + || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) { + // In this case it is safe to directly modify the internal ApplicationInfo state: + // - CompatibilityMode is global state, so will be the same for every call. + // - We only come in to here if the app should reported as installed; this is the + // default state, and we will do a copy otherwise. + // - The enable state will always be reported the same for the application across + // calls; the only exception is for the UNTIL_USED mode, and in that case we will + // be doing a copy. + updateApplicationInfo(p.applicationInfo, flags, state); return p.applicationInfo; } @@ -3565,26 +3581,12 @@ public class PackageParser { if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) { ai.sharedLibraryFiles = p.usesLibraryFiles; } - if (!sCompatibilityModeEnabled) { - ai.disableCompatibilityMode(); - } if (state.stopped) { ai.flags |= ApplicationInfo.FLAG_STOPPED; } else { ai.flags &= ~ApplicationInfo.FLAG_STOPPED; } - if (state.installed) { - ai.flags |= ApplicationInfo.FLAG_INSTALLED; - } else { - ai.flags &= ~ApplicationInfo.FLAG_INSTALLED; - } - if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { - ai.enabled = true; - } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED - || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { - ai.enabled = false; - } - ai.enabledSetting = state.enabled; + updateApplicationInfo(ai, flags, state); return ai; } diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java index f3a98db..8a43472 100644 --- a/core/java/android/content/pm/ParceledListSlice.java +++ b/core/java/android/content/pm/ParceledListSlice.java @@ -16,44 +16,92 @@ package android.content.pm; +import android.os.Binder; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; +import java.util.ArrayList; import java.util.List; /** - * Builds up a parcel that is discarded when written to another parcel or - * written to a list. This is useful for API that sends huge lists across a - * Binder that may be larger than the IPC limit. + * Transfer a large list of Parcelable objects across an IPC. Splits into + * multiple transactions if needed. * * @hide */ public class ParceledListSlice<T extends Parcelable> implements Parcelable { + private static String TAG = "ParceledListSlice"; + private static boolean DEBUG = false; + /* * TODO get this number from somewhere else. For now set it to a quarter of * the 1MB limit. */ private static final int MAX_IPC_SIZE = 256 * 1024; + private static final int MAX_FIRST_IPC_SIZE = MAX_IPC_SIZE / 2; - private Parcel mParcel; - - private int mNumItems; + private final List<T> mList; - private boolean mIsLastSlice; + public ParceledListSlice(List<T> list) { + mList = list; + } - public ParceledListSlice() { - mParcel = Parcel.obtain(); + private ParceledListSlice(Parcel p, ClassLoader loader) { + final int N = p.readInt(); + mList = new ArrayList<T>(N); + if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); + if (N <= 0) { + return; + } + Parcelable.Creator<T> creator = p.readParcelableCreator(loader); + int i = 0; + while (i < N) { + if (p.readInt() == 0) { + break; + } + mList.add(p.readCreator(creator, loader)); + if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); + i++; + } + if (i >= N) { + return; + } + final IBinder retriever = p.readStrongBinder(); + while (i < N) { + if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInt(i); + try { + retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); + return; + } + while (i < N && reply.readInt() != 0) { + mList.add(reply.readCreator(creator, loader)); + if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); + i++; + } + reply.recycle(); + data.recycle(); + } } - private ParceledListSlice(Parcel p, int numItems, boolean lastSlice) { - mParcel = p; - mNumItems = numItems; - mIsLastSlice = lastSlice; + public List<T> getList() { + return mList; } @Override public int describeContents() { - return 0; + int contents = 0; + for (int i=0; i<mList.size(); i++) { + contents |= mList.get(i).describeContents(); + } + return contents; } /** @@ -63,104 +111,59 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable { */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mNumItems); - dest.writeInt(mIsLastSlice ? 1 : 0); - - if (mNumItems > 0) { - final int parcelSize = mParcel.dataSize(); - dest.writeInt(parcelSize); - dest.appendFrom(mParcel, 0, parcelSize); - } - - mNumItems = 0; - mParcel.recycle(); - mParcel = null; - } - - /** - * Appends a parcel to this list slice. - * - * @param item Parcelable item to append to this list slice - * @return true when the list slice is full and should not be appended to - * anymore - */ - public boolean append(T item) { - if (mParcel == null) { - throw new IllegalStateException("ParceledListSlice has already been recycled"); - } - - item.writeToParcel(mParcel, PARCELABLE_WRITE_RETURN_VALUE); - mNumItems++; - - return mParcel.dataSize() > MAX_IPC_SIZE; - } - - /** - * Populates a list and discards the internal state of the - * ParceledListSlice in the process. The instance should - * not be used anymore. - * - * @param list list to insert items from this slice. - * @param creator creator that knows how to unparcel the - * target object type. - * @return the last item inserted into the list or null if none. - */ - public T populateList(List<T> list, Creator<T> creator) { - mParcel.setDataPosition(0); - - T item = null; - for (int i = 0; i < mNumItems; i++) { - item = creator.createFromParcel(mParcel); - list.add(item); + final int N = mList.size(); + final int callFlags = flags; + dest.writeInt(N); + if (DEBUG) Log.d(TAG, "Writing " + N + " items"); + if (N > 0) { + dest.writeParcelableCreator(mList.get(0)); + int i = 0; + while (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) { + dest.writeInt(1); + mList.get(i).writeToParcel(dest, callFlags); + if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); + i++; + } + if (i < N) { + dest.writeInt(0); + Binder retriever = new Binder() { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code != FIRST_CALL_TRANSACTION) { + return super.onTransact(code, data, reply, flags); + } + int i = data.readInt(); + if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); + while (i < N && reply.dataSize() < MAX_IPC_SIZE) { + reply.writeInt(1); + mList.get(i).writeToParcel(reply, callFlags); + if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); + i++; + } + if (i < N) { + if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); + reply.writeInt(0); + } + return true; + } + }; + if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); + dest.writeStrongBinder(retriever); + } } - - mParcel.recycle(); - mParcel = null; - - return item; - } - - /** - * Sets whether this is the last list slice in the series. - * - * @param lastSlice - */ - public void setLastSlice(boolean lastSlice) { - mIsLastSlice = lastSlice; - } - - /** - * Returns whether this is the last slice in a series of slices. - * - * @return true if this is the last slice in the series. - */ - public boolean isLastSlice() { - return mIsLastSlice; } @SuppressWarnings("unchecked") - public static final Parcelable.Creator<ParceledListSlice> CREATOR = - new Parcelable.Creator<ParceledListSlice>() { + public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR = + new Parcelable.ClassLoaderCreator<ParceledListSlice>() { public ParceledListSlice createFromParcel(Parcel in) { - final int numItems = in.readInt(); - final boolean lastSlice = in.readInt() == 1; - - if (numItems > 0) { - final int parcelSize = in.readInt(); - - // Advance within this Parcel - int offset = in.dataPosition(); - in.setDataPosition(offset + parcelSize); - - Parcel p = Parcel.obtain(); - p.setDataPosition(0); - p.appendFrom(in, offset, parcelSize); - p.setDataPosition(0); + return new ParceledListSlice(in, null); + } - return new ParceledListSlice(p, numItems, lastSlice); - } else { - return new ParceledListSlice(); - } + @Override + public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { + return new ParceledListSlice(in, loader); } public ParceledListSlice[] newArray(int size) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index b316f23..24a0bb5 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -77,7 +77,7 @@ public class Resources { private static final int ID_OTHER = 0x01000004; - private static final Object mSync = new Object(); + private static final Object sSync = new Object(); /*package*/ static Resources mSystem = null; // Information about preloaded resources. Note that they are not @@ -92,17 +92,18 @@ public class Resources { private static boolean sPreloaded; private static int sPreloadedDensity; - /*package*/ final TypedValue mTmpValue = new TypedValue(); - /*package*/ final Configuration mTmpConfig = new Configuration(); + // These are protected by mAccessLock. - // These are protected by the mTmpValue lock. - private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache + /*package*/ final Object mAccessLock = new Object(); + /*package*/ final Configuration mTmpConfig = new Configuration(); + /*package*/ TypedValue mTmpValue = new TypedValue(); + /*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); - private final LongSparseArray<WeakReference<ColorStateList> > mColorStateListCache + /*package*/ final LongSparseArray<WeakReference<ColorStateList> > mColorStateListCache = new LongSparseArray<WeakReference<ColorStateList> >(); - private final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache + /*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); - private boolean mPreloading; + /*package*/ boolean mPreloading; /*package*/ TypedArray mCachedStyledAttributes = null; RuntimeException mLastRetrievedAttrs = null; @@ -196,7 +197,7 @@ public class Resources { * on orientation, etc). */ public static Resources getSystem() { - synchronized (mSync) { + synchronized (sSync) { Resources ret = mSystem; if (ret == null) { ret = new Resources(); @@ -266,7 +267,7 @@ public class Resources { } private NativePluralRules getPluralRule() { - synchronized (mSync) { + synchronized (sSync) { if (mPluralRule == null) { mPluralRule = NativePluralRules.forLocale(mConfiguration.locale); } @@ -517,8 +518,11 @@ public class Resources { * @see #getDimensionPixelSize */ public float getDimension(int id) throws NotFoundException { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimension(value.data, mMetrics); @@ -549,8 +553,11 @@ public class Resources { * @see #getDimensionPixelSize */ public int getDimensionPixelOffset(int id) throws NotFoundException { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimensionPixelOffset( @@ -583,8 +590,11 @@ public class Resources { * @see #getDimensionPixelOffset */ public int getDimensionPixelSize(int id) throws NotFoundException { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimensionPixelSize( @@ -614,8 +624,11 @@ public class Resources { * @throws NotFoundException Throws NotFoundException if the given ID does not exist. */ public float getFraction(int id, int base, int pbase) { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type == TypedValue.TYPE_FRACTION) { return TypedValue.complexToFraction(value.data, base, pbase); @@ -654,11 +667,23 @@ public class Resources { * @return Drawable An object that can be used to draw this resource. */ public Drawable getDrawable(int id) throws NotFoundException { - synchronized (mTmpValue) { - TypedValue value = mTmpValue; + TypedValue value; + synchronized (mAccessLock) { + value = mTmpValue; + if (value == null) { + value = new TypedValue(); + } else { + mTmpValue = null; + } getValue(id, value, true); - return loadDrawable(value, id); } + Drawable res = loadDrawable(value, id); + synchronized (mAccessLock) { + if (mTmpValue == null) { + mTmpValue = value; + } + } + return res; } /** @@ -681,8 +706,14 @@ public class Resources { * @return Drawable An object that can be used to draw this resource. */ public Drawable getDrawableForDensity(int id, int density) throws NotFoundException { - synchronized (mTmpValue) { - TypedValue value = mTmpValue; + TypedValue value; + synchronized (mAccessLock) { + value = mTmpValue; + if (value == null) { + value = new TypedValue(); + } else { + mTmpValue = null; + } getValueForDensity(id, density, value, true); /* @@ -699,9 +730,15 @@ public class Resources { value.density = (value.density * mMetrics.densityDpi) / density; } } + } - return loadDrawable(value, id); + Drawable res = loadDrawable(value, id); + synchronized (mAccessLock) { + if (mTmpValue == null) { + mTmpValue = value; + } } + return res; } /** @@ -739,20 +776,31 @@ public class Resources { * @return Returns a single color value in the form 0xAARRGGBB. */ public int getColor(int id) throws NotFoundException { - synchronized (mTmpValue) { - TypedValue value = mTmpValue; + TypedValue value; + synchronized (mAccessLock) { + value = mTmpValue; + if (value == null) { + value = new TypedValue(); + } getValue(id, value, true); if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) { + mTmpValue = value; return value.data; - } else if (value.type == TypedValue.TYPE_STRING) { - ColorStateList csl = loadColorStateList(mTmpValue, id); - return csl.getDefaultColor(); + } else if (value.type != TypedValue.TYPE_STRING) { + throw new NotFoundException( + "Resource ID #0x" + Integer.toHexString(id) + " type #0x" + + Integer.toHexString(value.type) + " is not valid"); + } + mTmpValue = null; + } + ColorStateList csl = loadColorStateList(value, id); + synchronized (mAccessLock) { + if (mTmpValue == null) { + mTmpValue = value; } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); } + return csl.getDefaultColor(); } /** @@ -770,11 +818,23 @@ public class Resources { * solid color or multiple colors that can be selected based on a state. */ public ColorStateList getColorStateList(int id) throws NotFoundException { - synchronized (mTmpValue) { - TypedValue value = mTmpValue; + TypedValue value; + synchronized (mAccessLock) { + value = mTmpValue; + if (value == null) { + value = new TypedValue(); + } else { + mTmpValue = null; + } getValue(id, value, true); - return loadColorStateList(value, id); } + ColorStateList res = loadColorStateList(value, id); + synchronized (mAccessLock) { + if (mTmpValue == null) { + mTmpValue = value; + } + } + return res; } /** @@ -791,8 +851,11 @@ public class Resources { * @return Returns the boolean value contained in the resource. */ public boolean getBoolean(int id) throws NotFoundException { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) { @@ -816,8 +879,11 @@ public class Resources { * @return Returns the integer value contained in the resource. */ public int getInteger(int id) throws NotFoundException { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) { @@ -917,9 +983,22 @@ public class Resources { * */ public InputStream openRawResource(int id) throws NotFoundException { - synchronized (mTmpValue) { - return openRawResource(id, mTmpValue); + TypedValue value; + synchronized (mAccessLock) { + value = mTmpValue; + if (value == null) { + value = new TypedValue(); + } else { + mTmpValue = null; + } + } + InputStream res = openRawResource(id, value); + synchronized (mAccessLock) { + if (mTmpValue == null) { + mTmpValue = value; + } } + return res; } /** @@ -971,22 +1050,32 @@ public class Resources { * */ public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException { - synchronized (mTmpValue) { - TypedValue value = mTmpValue; + TypedValue value; + synchronized (mAccessLock) { + value = mTmpValue; + if (value == null) { + value = new TypedValue(); + } else { + mTmpValue = null; + } getValue(id, value, true); - - try { - return mAssets.openNonAssetFd( - value.assetCookie, value.string.toString()); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + value.string.toString() - + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; + } + try { + return mAssets.openNonAssetFd( + value.assetCookie, value.string.toString()); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException( + "File " + value.string.toString() + + " from drawable resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; + } finally { + synchronized (mAccessLock) { + if (mTmpValue == null) { + mTmpValue = value; + } } - } } @@ -1118,7 +1207,7 @@ public class Resources { } /** - * Return a StyledAttributes holding the values defined by + * Return a TypedArray holding the values defined by * <var>Theme</var> which are listed in <var>attrs</var>. * * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done @@ -1146,7 +1235,7 @@ public class Resources { } /** - * Return a StyledAttributes holding the values defined by the style + * Return a TypedArray holding the values defined by the style * resource <var>resid</var> which are listed in <var>attrs</var>. * * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done @@ -1203,7 +1292,7 @@ public class Resources { } /** - * Return a StyledAttributes holding the attribute values in + * Return a TypedArray holding the attribute values in * <var>set</var> * that are listed in <var>attrs</var>. In addition, if the given * AttributeSet specifies a style class (through the "style" attribute), @@ -1235,10 +1324,10 @@ public class Resources { * @param attrs The desired attributes to be retrieved. * @param defStyleAttr An attribute in the current theme that contains a * reference to a style resource that supplies - * defaults values for the StyledAttributes. Can be + * defaults values for the TypedArray. Can be * 0 to not look for defaults. * @param defStyleRes A resource identifier of a style resource that - * supplies default values for the StyledAttributes, + * supplies default values for the TypedArray, * used only if defStyleAttr is 0 or can not be found * in the theme. Can be 0 to not look for defaults. * @@ -1407,7 +1496,7 @@ public class Resources { */ public void updateConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat) { - synchronized (mTmpValue) { + synchronized (mAccessLock) { if (false) { Slog.i(TAG, "**** Updating config of " + this + ": old config is " + mConfiguration + " old compat is " + mCompatibilityInfo); @@ -1497,21 +1586,21 @@ public class Resources { + " final compat is " + mCompatibilityInfo); } - clearDrawableCache(mDrawableCache, configChanges); - clearDrawableCache(mColorDrawableCache, configChanges); + clearDrawableCacheLocked(mDrawableCache, configChanges); + clearDrawableCacheLocked(mColorDrawableCache, configChanges); mColorStateListCache.clear(); flushLayoutCache(); } - synchronized (mSync) { + synchronized (sSync) { if (mPluralRule != null) { mPluralRule = NativePluralRules.forLocale(config.locale); } } } - private void clearDrawableCache( + private void clearDrawableCacheLocked( LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) { int N = cache.size(); @@ -1631,6 +1720,9 @@ public class Resources { * resource was found. (0 is not a valid resource ID.) */ public int getIdentifier(String name, String defType, String defPackage) { + if (name == null) { + throw new NullPointerException("name is null"); + } try { return Integer.parseInt(name); } catch (Exception e) { @@ -1846,7 +1938,7 @@ public class Resources { * {@hide} */ public final void startPreloading() { - synchronized (mSync) { + synchronized (sSync) { if (sPreloaded) { throw new IllegalStateException("Resources already preloaded"); } @@ -1990,7 +2082,7 @@ public class Resources { } } } else { - synchronized (mTmpValue) { + synchronized (mAccessLock) { //Log.i(TAG, "Saving cached drawable @ #" + // Integer.toHexString(key.intValue()) // + " in " + this + ": " + cs); @@ -2010,7 +2102,7 @@ public class Resources { private Drawable getCachedDrawable( LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { - synchronized (mTmpValue) { + synchronized (mAccessLock) { WeakReference<Drawable.ConstantState> wr = drawableCache.get(key); if (wr != null) { // we have the key Drawable.ConstantState entry = wr.get(); @@ -2102,7 +2194,7 @@ public class Resources { sPreloadedColorStateLists.put(key, csl); } } else { - synchronized (mTmpValue) { + synchronized (mAccessLock) { //Log.i(TAG, "Saving cached color state list @ #" + // Integer.toHexString(key.intValue()) // + " in " + this + ": " + csl); @@ -2115,7 +2207,7 @@ public class Resources { } private ColorStateList getCachedColorStateList(long key) { - synchronized (mTmpValue) { + synchronized (mAccessLock) { WeakReference<ColorStateList> wr = mColorStateListCache.get(key); if (wr != null) { // we have the key ColorStateList entry = wr.get(); @@ -2134,8 +2226,11 @@ public class Resources { /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type) throws NotFoundException { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } getValue(id, value, true); if (value.type == TypedValue.TYPE_STRING) { return loadXmlResourceParser(value.string.toString(), id, @@ -2197,7 +2292,7 @@ public class Resources { } private TypedArray getCachedStyledAttributes(int len) { - synchronized (mTmpValue) { + synchronized (mAccessLock) { TypedArray attrs = mCachedStyledAttributes; if (attrs != null) { mCachedStyledAttributes = null; diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index 63e33ce..78180b1 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.graphics.Color; import android.text.*; import android.text.style.*; import android.util.Log; @@ -24,7 +25,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; -import com.android.internal.util.XmlUtils; +import java.util.Arrays; /** * Conveniences for retrieving data out of a compiled string resource. @@ -33,7 +34,7 @@ import com.android.internal.util.XmlUtils; */ final class StringBlock { private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; + private static final boolean localLOGV = false; private final int mNative; private final boolean mUseSparse; @@ -82,7 +83,7 @@ final class StringBlock { CharSequence res = str; int[] style = nativeGetStyle(mNative, idx); if (localLOGV) Log.v(TAG, "Got string: " + str); - if (localLOGV) Log.v(TAG, "Got styles: " + style); + if (localLOGV) Log.v(TAG, "Got styles: " + Arrays.toString(style)); if (style != null) { if (mStyleIDs == null) { mStyleIDs = new StyleIDs(); @@ -139,8 +140,12 @@ final class StringBlock { } protected void finalize() throws Throwable { - if (mOwnsNative) { - nativeDestroy(mNative); + try { + super.finalize(); + } finally { + if (mOwnsNative) { + nativeDestroy(mNative); + } } } @@ -236,19 +241,31 @@ final class StringBlock { sub = subtag(tag, ";fgcolor="); if (sub != null) { - int color = XmlUtils.convertValueToUnsignedInt(sub, -1); - buffer.setSpan(new ForegroundColorSpan(color), + buffer.setSpan(getColor(sub, true), style[i+1], style[i+2]+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } + sub = subtag(tag, ";color="); + if (sub != null) { + buffer.setSpan(getColor(sub, true), + style[i+1], style[i+2]+1, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + sub = subtag(tag, ";bgcolor="); if (sub != null) { - int color = XmlUtils.convertValueToUnsignedInt(sub, -1); - buffer.setSpan(new BackgroundColorSpan(color), + buffer.setSpan(getColor(sub, false), style[i+1], style[i+2]+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } + + sub = subtag(tag, ";face="); + if (sub != null) { + buffer.setSpan(new TypefaceSpan(sub), + style[i+1], style[i+2]+1, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } } else if (tag.startsWith("a;")) { String sub; @@ -289,6 +306,48 @@ final class StringBlock { } /** + * Returns a span for the specified color string representation. + * If the specified string does not represent a color (null, empty, etc.) + * the color black is returned instead. + * + * @param color The color as a string. Can be a resource reference, + * HTML hexadecimal, octal or a name + * @param foreground True if the color will be used as the foreground color, + * false otherwise + * + * @return A CharacterStyle + * + * @see Color#getHtmlColor(String) + */ + private static CharacterStyle getColor(String color, boolean foreground) { + int c = 0xff000000; + + if (!TextUtils.isEmpty(color)) { + if (color.startsWith("@")) { + Resources res = Resources.getSystem(); + String name = color.substring(1); + int colorRes = res.getIdentifier(name, "color", "android"); + if (colorRes != 0) { + ColorStateList colors = res.getColorStateList(colorRes); + if (foreground) { + return new TextAppearanceSpan(null, 0, 0, colors, null); + } else { + c = colors.getDefaultColor(); + } + } + } else { + c = Color.getHtmlColor(color); + } + } + + if (foreground) { + return new ForegroundColorSpan(c); + } else { + return new BackgroundColorSpan(c); + } + } + + /** * If a translator has messed up the edges of paragraph-level markup, * fix it to actually cover the entire paragraph that it is attached to * instead of just whatever range they put it on. @@ -423,11 +482,11 @@ final class StringBlock { + ": " + nativeGetSize(mNative)); } - private static final native int nativeCreate(byte[] data, + private static native int nativeCreate(byte[] data, int offset, int size); - private static final native int nativeGetSize(int obj); - private static final native String nativeGetString(int obj, int idx); - private static final native int[] nativeGetStyle(int obj, int idx); - private static final native void nativeDestroy(int obj); + private static native int nativeGetSize(int obj); + private static native String nativeGetString(int obj, int idx); + private static native int[] nativeGetStyle(int obj, int idx); + private static native void nativeDestroy(int obj); } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 2968fbb..27dddd4 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -687,7 +687,7 @@ public class TypedArray { * Give back a previously retrieved array, for later re-use. */ public void recycle() { - synchronized (mResources.mTmpValue) { + synchronized (mResources.mAccessLock) { TypedArray cached = mResources.mCachedStyledAttributes; if (cached == null || cached.mData.length < mData.length) { mXml = null; diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java index 525be96..82a61d4 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -132,6 +132,11 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } + /** + * Returns an object that contains sufficient metadata to reconstruct + * the cursor remotely. May throw if an error occurs when executing the query + * and obtaining the row count. + */ public BulkCursorDescriptor getBulkCursorDescriptor() { synchronized (mLock) { throwIfCursorIsClosed(); diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index b29897e..5a1a8e2 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -138,17 +138,26 @@ public class SQLiteCursor extends AbstractWindowedCursor { private void fillWindow(int requiredPos) { clearOrCreateWindow(getDatabase().getPath()); - if (mCount == NO_COUNT) { - int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); - mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); - mCursorWindowCapacity = mWindow.getNumRows(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "received count(*) from native_fill_window: " + mCount); + try { + if (mCount == NO_COUNT) { + int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); + mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); + mCursorWindowCapacity = mWindow.getNumRows(); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "received count(*) from native_fill_window: " + mCount); + } + } else { + int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, + mCursorWindowCapacity); + mQuery.fillWindow(mWindow, startPos, requiredPos, false); } - } else { - int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, - mCursorWindowCapacity); - mQuery.fillWindow(mWindow, startPos, requiredPos, false); + } catch (RuntimeException ex) { + // Close the cursor window if the query failed and therefore will + // not produce any results. This helps to avoid accidentally leaking + // the cursor window if the client does not correctly handle exceptions + // and fails to close the cursor. + closeWindow(); + throw ex; } } diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index e99fa92..842a482 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -36,6 +36,10 @@ public class DdmHandleHello extends ChunkHandler { private static DdmHandleHello mInstance = new DdmHandleHello(); + private static final String[] FRAMEWORK_FEATURES = new String[] { + "opengl-tracing", + "view-hierarchy", + }; /* singleton, do not instantiate */ private DdmHandleHello() {} @@ -149,21 +153,27 @@ public class DdmHandleHello extends ChunkHandler { private Chunk handleFEAT(Chunk request) { // TODO: query the VM to ensure that support for these features // is actually compiled in - final String[] features = Debug.getVmFeatureList(); + final String[] vmFeatures = Debug.getVmFeatureList(); if (false) Log.v("ddm-heap", "Got feature list request"); - int size = 4 + 4 * features.length; - for (int i = features.length-1; i >= 0; i--) - size += features[i].length() * 2; + int size = 4 + 4 * (vmFeatures.length + FRAMEWORK_FEATURES.length); + for (int i = vmFeatures.length-1; i >= 0; i--) + size += vmFeatures[i].length() * 2; + for (int i = FRAMEWORK_FEATURES.length-1; i>= 0; i--) + size += FRAMEWORK_FEATURES[i].length() * 2; ByteBuffer out = ByteBuffer.allocate(size); out.order(ChunkHandler.CHUNK_ORDER); - out.putInt(features.length); - for (int i = features.length-1; i >= 0; i--) { - out.putInt(features[i].length()); - putString(out, features[i]); + out.putInt(vmFeatures.length + FRAMEWORK_FEATURES.length); + for (int i = vmFeatures.length-1; i >= 0; i--) { + out.putInt(vmFeatures[i].length()); + putString(out, vmFeatures[i]); + } + for (int i = FRAMEWORK_FEATURES.length-1; i >= 0; i--) { + out.putInt(FRAMEWORK_FEATURES[i].length()); + putString(out, FRAMEWORK_FEATURES[i]); } return new Chunk(CHUNK_FEAT, out); diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java new file mode 100644 index 0000000..ce83796 --- /dev/null +++ b/core/java/android/ddm/DdmHandleViewDebug.java @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.ddm; + +import android.opengl.GLUtils; +import android.util.Log; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; + +import org.apache.harmony.dalvik.ddmc.Chunk; +import org.apache.harmony.dalvik.ddmc.ChunkHandler; +import org.apache.harmony.dalvik.ddmc.DdmServer; + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.lang.reflect.Method; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +/** + * Handle various requests related to profiling / debugging of the view system. + * Support for these features are advertised via {@link DdmHandleHello}. + */ +public class DdmHandleViewDebug extends ChunkHandler { + /** Enable/Disable tracing of OpenGL calls. */ + public static final int CHUNK_VUGL = type("VUGL"); + + /** List {@link ViewRootImpl}'s of this process. */ + private static final int CHUNK_VULW = type("VULW"); + + /** Operation on view root, first parameter in packet should be one of VURT_* constants */ + private static final int CHUNK_VURT = type("VURT"); + + /** Dump view hierarchy. */ + private static final int VURT_DUMP_HIERARCHY = 1; + + /** Capture View Layers. */ + private static final int VURT_CAPTURE_LAYERS = 2; + + /** + * Generic View Operation, first parameter in the packet should be one of the + * VUOP_* constants below. + */ + private static final int CHUNK_VUOP = type("VUOP"); + + /** Capture View. */ + private static final int VUOP_CAPTURE_VIEW = 1; + + /** Obtain the Display List corresponding to the view. */ + private static final int VUOP_DUMP_DISPLAYLIST = 2; + + /** Profile a view. */ + private static final int VUOP_PROFILE_VIEW = 3; + + /** Invoke a method on the view. */ + private static final int VUOP_INVOKE_VIEW_METHOD = 4; + + /** Set layout parameter. */ + private static final int VUOP_SET_LAYOUT_PARAMETER = 5; + + /** Error code indicating operation specified in chunk is invalid. */ + private static final int ERR_INVALID_OP = -1; + + /** Error code indicating that the parameters are invalid. */ + private static final int ERR_INVALID_PARAM = -2; + + /** Error code indicating an exception while performing operation. */ + private static final int ERR_EXCEPTION = -3; + + private static final String TAG = "DdmViewDebug"; + + private static final DdmHandleViewDebug sInstance = new DdmHandleViewDebug(); + + /** singleton, do not instantiate. */ + private DdmHandleViewDebug() {} + + public static void register() { + DdmServer.registerHandler(CHUNK_VUGL, sInstance); + DdmServer.registerHandler(CHUNK_VULW, sInstance); + DdmServer.registerHandler(CHUNK_VURT, sInstance); + DdmServer.registerHandler(CHUNK_VUOP, sInstance); + } + + @Override + public void connected() { + } + + @Override + public void disconnected() { + } + + @Override + public Chunk handleChunk(Chunk request) { + int type = request.type; + + if (type == CHUNK_VUGL) { + return handleOpenGlTrace(request); + } else if (type == CHUNK_VULW) { + return listWindows(); + } + + ByteBuffer in = wrapChunk(request); + int op = in.getInt(); + + View rootView = getRootView(in); + if (rootView == null) { + return createFailChunk(ERR_INVALID_PARAM, "Invalid View Root"); + } + + if (type == CHUNK_VURT) { + if (op == VURT_DUMP_HIERARCHY) + return dumpHierarchy(rootView, in); + else if (op == VURT_CAPTURE_LAYERS) + return captureLayers(rootView); + else + return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op); + } + + final View targetView = getTargetView(rootView, in); + if (targetView == null) { + return createFailChunk(ERR_INVALID_PARAM, "Invalid target view"); + } + + if (type == CHUNK_VUOP) { + switch (op) { + case VUOP_CAPTURE_VIEW: + return captureView(rootView, targetView); + case VUOP_DUMP_DISPLAYLIST: + return dumpDisplayLists(rootView, targetView); + case VUOP_PROFILE_VIEW: + return profileView(rootView, targetView); + case VUOP_INVOKE_VIEW_METHOD: + return invokeViewMethod(rootView, targetView, in); + case VUOP_SET_LAYOUT_PARAMETER: + return setLayoutParameter(rootView, targetView, in); + default: + return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op); + } + } else { + throw new RuntimeException("Unknown packet " + ChunkHandler.name(type)); + } + } + + private Chunk handleOpenGlTrace(Chunk request) { + ByteBuffer in = wrapChunk(request); + GLUtils.setTracingLevel(in.getInt()); + return null; // empty response + } + + /** Returns the list of windows owned by this client. */ + private Chunk listWindows() { + String[] windowNames = WindowManagerGlobal.getInstance().getViewRootNames(); + + int responseLength = 4; // # of windows + for (String name : windowNames) { + responseLength += 4; // length of next window name + responseLength += name.length() * 2; // window name + } + + ByteBuffer out = ByteBuffer.allocate(responseLength); + out.order(ChunkHandler.CHUNK_ORDER); + + out.putInt(windowNames.length); + for (String name : windowNames) { + out.putInt(name.length()); + putString(out, name); + } + + return new Chunk(CHUNK_VULW, out); + } + + private View getRootView(ByteBuffer in) { + try { + int viewRootNameLength = in.getInt(); + String viewRootName = getString(in, viewRootNameLength); + return WindowManagerGlobal.getInstance().getRootView(viewRootName); + } catch (BufferUnderflowException e) { + return null; + } + } + + private View getTargetView(View root, ByteBuffer in) { + int viewLength; + String viewName; + + try { + viewLength = in.getInt(); + viewName = getString(in, viewLength); + } catch (BufferUnderflowException e) { + return null; + } + + return ViewDebug.findView(root, viewName); + } + + /** + * Returns the view hierarchy and/or view properties starting at the provided view. + * Based on the input options, the return data may include: + * - just the view hierarchy + * - view hierarchy & the properties for each of the views + * - just the view properties for a specific view. + * TODO: Currently this only returns views starting at the root, need to fix so that + * it can return properties of any view. + */ + private Chunk dumpHierarchy(View rootView, ByteBuffer in) { + boolean skipChildren = in.getInt() > 0; + boolean includeProperties = in.getInt() > 0; + + ByteArrayOutputStream b = new ByteArrayOutputStream(1024); + try { + ViewDebug.dump(rootView, skipChildren, includeProperties, b); + } catch (IOException e) { + return createFailChunk(1, "Unexpected error while obtaining view hierarchy: " + + e.getMessage()); + } + + byte[] data = b.toByteArray(); + return new Chunk(CHUNK_VURT, data, 0, data.length); + } + + /** Returns a buffer with region details & bitmap of every single view. */ + private Chunk captureLayers(View rootView) { + ByteArrayOutputStream b = new ByteArrayOutputStream(1024); + DataOutputStream dos = new DataOutputStream(b); + try { + ViewDebug.captureLayers(rootView, dos); + } catch (IOException e) { + return createFailChunk(1, "Unexpected error while obtaining view hierarchy: " + + e.getMessage()); + } finally { + try { + dos.close(); + } catch (IOException e) { + // ignore + } + } + + byte[] data = b.toByteArray(); + return new Chunk(CHUNK_VURT, data, 0, data.length); + } + + private Chunk captureView(View rootView, View targetView) { + ByteArrayOutputStream b = new ByteArrayOutputStream(1024); + try { + ViewDebug.capture(rootView, b, targetView); + } catch (IOException e) { + return createFailChunk(1, "Unexpected error while capturing view: " + + e.getMessage()); + } + + byte[] data = b.toByteArray(); + return new Chunk(CHUNK_VUOP, data, 0, data.length); + } + + /** Returns the display lists corresponding to the provided view. */ + private Chunk dumpDisplayLists(final View rootView, final View targetView) { + rootView.post(new Runnable() { + @Override + public void run() { + ViewDebug.outputDisplayList(rootView, targetView); + } + }); + return null; + } + + /** + * Invokes provided method on the view. + * The method name and its arguments are passed in as inputs via the byte buffer. + * The buffer contains:<ol> + * <li> len(method name) </li> + * <li> method name </li> + * <li> # of args </li> + * <li> arguments: Each argument comprises of a type specifier followed by the actual argument. + * The type specifier is a single character as used in JNI: + * (Z - boolean, B - byte, C - char, S - short, I - int, J - long, + * F - float, D - double). <p> + * The type specifier is followed by the actual value of argument. + * Booleans are encoded via bytes with 0 indicating false.</li> + * </ol> + * Methods that take no arguments need only specify the method name. + */ + private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) { + int l = in.getInt(); + String methodName = getString(in, l); + + Class<?>[] argTypes; + Object[] args; + if (!in.hasRemaining()) { + argTypes = new Class<?>[0]; + args = new Object[0]; + } else { + int nArgs = in.getInt(); + + argTypes = new Class<?>[nArgs]; + args = new Object[nArgs]; + + for (int i = 0; i < nArgs; i++) { + char c = in.getChar(); + switch (c) { + case 'Z': + argTypes[i] = boolean.class; + args[i] = in.get() == 0 ? false : true; + break; + case 'B': + argTypes[i] = byte.class; + args[i] = in.get(); + break; + case 'C': + argTypes[i] = char.class; + args[i] = in.getChar(); + break; + case 'S': + argTypes[i] = short.class; + args[i] = in.getShort(); + break; + case 'I': + argTypes[i] = int.class; + args[i] = in.getInt(); + break; + case 'J': + argTypes[i] = long.class; + args[i] = in.getLong(); + break; + case 'F': + argTypes[i] = float.class; + args[i] = in.getFloat(); + break; + case 'D': + argTypes[i] = double.class; + args[i] = in.getDouble(); + break; + default: + Log.e(TAG, "arg " + i + ", unrecognized type: " + c); + return createFailChunk(ERR_INVALID_PARAM, + "Unsupported parameter type (" + c + ") to invoke view method."); + } + } + } + + Method method = null; + try { + method = targetView.getClass().getMethod(methodName, argTypes); + } catch (NoSuchMethodException e) { + Log.e(TAG, "No such method: " + e.getMessage()); + return createFailChunk(ERR_INVALID_PARAM, + "No such method: " + e.getMessage()); + } + + try { + ViewDebug.invokeViewMethod(targetView, method, args); + } catch (Exception e) { + Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage()); + String msg = e.getCause().getMessage(); + if (msg == null) { + msg = e.getCause().toString(); + } + return createFailChunk(ERR_EXCEPTION, msg); + } + + return null; + } + + private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) { + int l = in.getInt(); + String param = getString(in, l); + int value = in.getInt(); + try { + ViewDebug.setLayoutParameter(targetView, param, value); + } catch (Exception e) { + Log.e(TAG, "Exception setting layout parameter: " + e); + return createFailChunk(ERR_EXCEPTION, "Error accessing field " + + param + ":" + e.getMessage()); + } + + return null; + } + + /** Profiles provided view. */ + private Chunk profileView(View rootView, final View targetView) { + ByteArrayOutputStream b = new ByteArrayOutputStream(32 * 1024); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(b), 32 * 1024); + try { + ViewDebug.profileViewAndChildren(targetView, bw); + } catch (IOException e) { + return createFailChunk(1, "Unexpected error while profiling view: " + e.getMessage()); + } finally { + try { + bw.close(); + } catch (IOException e) { + // ignore + } + } + + byte[] data = b.toByteArray(); + return new Chunk(CHUNK_VUOP, data, 0, data.length); + } +} diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java index ecd450d..e0faa51 100644 --- a/core/java/android/ddm/DdmRegister.java +++ b/core/java/android/ddm/DdmRegister.java @@ -51,6 +51,7 @@ public class DdmRegister { DdmHandleNativeHeap.register(); DdmHandleProfiling.register(); DdmHandleExit.register(); + DdmHandleViewDebug.register(); DdmServer.registrationComplete(); } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index e0c9d2c..41384d2 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -194,7 +194,8 @@ public final class Sensor { return mMinDelay; } - int getHandle() { + /** @hide */ + public int getHandle() { return mHandle; } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index b8ad818..c0d2fae 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1314,56 +1314,4 @@ public abstract class SensorManager { return mLegacySensorManager; } } - - /** - * Sensor event pool implementation. - * @hide - */ - protected static final class SensorEventPool { - private final int mPoolSize; - private final SensorEvent mPool[]; - private int mNumItemsInPool; - - private SensorEvent createSensorEvent() { - // maximal size for all legacy events is 3 - return new SensorEvent(3); - } - - SensorEventPool(int poolSize) { - mPoolSize = poolSize; - mNumItemsInPool = poolSize; - mPool = new SensorEvent[poolSize]; - } - - SensorEvent getFromPool() { - SensorEvent t = null; - synchronized (this) { - if (mNumItemsInPool > 0) { - // remove the "top" item from the pool - final int index = mPoolSize - mNumItemsInPool; - t = mPool[index]; - mPool[index] = null; - mNumItemsInPool--; - } - } - if (t == null) { - // the pool was empty or this item was removed from the pool for - // the first time. In any case, we need to create a new item. - t = createSensorEvent(); - } - return t; - } - - void returnToPool(SensorEvent t) { - synchronized (this) { - // is there space left in the pool? - if (mNumItemsInPool < mPoolSize) { - // if so, return the item to the pool - mNumItemsInPool++; - final int index = mPoolSize - mNumItemsInPool; - mPool[index] = t; - } - } - } - } } diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java index 5ef122b..f50cdef 100644 --- a/core/java/android/hardware/SerialPort.java +++ b/core/java/android/hardware/SerialPort.java @@ -82,7 +82,9 @@ public class SerialPort { } /** - * Reads data into the provided buffer + * Reads data into the provided buffer. + * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is + * unchanged after a call to this method. * * @param buffer to read into * @return number of bytes read @@ -98,7 +100,9 @@ public class SerialPort { } /** - * Writes data from provided buffer + * Writes data from provided buffer. + * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is + * unchanged after a call to this method. * * @param buffer to write * @param length number of bytes to write diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 7375e7d..9591631 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -16,18 +16,19 @@ package android.hardware; -import android.os.Looper; -import android.os.Process; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import dalvik.system.CloseGuard; + import android.os.Handler; -import android.os.Message; -import android.util.Log; +import android.os.Looper; +import android.os.MessageQueue; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; -import java.util.ArrayList; -import java.util.List; - /** * Sensor manager implementation that communicates with the built-in * system sensors. @@ -35,236 +36,43 @@ import java.util.List; * @hide */ public class SystemSensorManager extends SensorManager { - private static final int SENSOR_DISABLE = -1; + private static native void nativeClassInit(); + private static native int nativeGetNextSensor(Sensor sensor, int next); + private static boolean sSensorModuleInitialized = false; - private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>(); - /* The thread and the sensor list are global to the process - * but the actual thread is spawned on demand */ - private static SensorThread sSensorThread; - private static int sQueue; + private static final Object sSensorModuleLock = new Object(); + private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>(); + private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>(); - // Used within this module from outside SensorManager, don't make private - static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>(); - static final ArrayList<ListenerDelegate> sListeners = - new ArrayList<ListenerDelegate>(); + // Listener list + private final ArrayList<SensorEventListenerSensorPair> mListenerDelegates = new ArrayList<SensorEventListenerSensorPair>(); // Common pool of sensor events. - static SensorEventPool sPool; + private static SensorEventPool sPool; // Looper associated with the context in which this instance was created. - final Looper mMainLooper; - - /*-----------------------------------------------------------------------*/ - - static private class SensorThread { - - Thread mThread; - boolean mSensorsReady; - - SensorThread() { - } - - @Override - protected void finalize() { - } - - // must be called with sListeners lock - boolean startLocked() { - try { - if (mThread == null) { - mSensorsReady = false; - SensorThreadRunnable runnable = new SensorThreadRunnable(); - Thread thread = new Thread(runnable, SensorThread.class.getName()); - thread.start(); - synchronized (runnable) { - while (mSensorsReady == false) { - runnable.wait(); - } - } - mThread = thread; - } - } catch (InterruptedException e) { - } - return mThread == null ? false : true; - } - - private class SensorThreadRunnable implements Runnable { - SensorThreadRunnable() { - } + private final Looper mMainLooper; - private boolean open() { - // NOTE: this cannot synchronize on sListeners, since - // it's held in the main thread at least until we - // return from here. - sQueue = sensors_create_queue(); - return true; - } + // maps a SensorEventListener to a SensorEventQueue + private final Hashtable<SensorEventListener, SensorEventQueue> mSensorEventQueueMap; - public void run() { - //Log.d(TAG, "entering main sensor thread"); - final float[] values = new float[3]; - final int[] status = new int[1]; - final long timestamp[] = new long[1]; - Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); - - if (!open()) { - return; - } - - synchronized (this) { - // we've open the driver, we're ready to open the sensors - mSensorsReady = true; - this.notify(); - } - - while (true) { - // wait for an event - final int sensor = sensors_data_poll(sQueue, values, status, timestamp); - - int accuracy = status[0]; - synchronized (sListeners) { - if (sensor == -1 || sListeners.isEmpty()) { - // we lost the connection to the event stream. this happens - // when the last listener is removed or if there is an error - if (sensor == -1 && !sListeners.isEmpty()) { - // log a warning in case of abnormal termination - Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor); - } - // we have no more listeners or polling failed, terminate the thread - sensors_destroy_queue(sQueue); - sQueue = 0; - mThread = null; - break; - } - final Sensor sensorObject = sHandleToSensor.get(sensor); - if (sensorObject != null) { - // report the sensor event to all listeners that - // care about it. - final int size = sListeners.size(); - for (int i=0 ; i<size ; i++) { - ListenerDelegate listener = sListeners.get(i); - if (listener.hasSensor(sensorObject)) { - // this is asynchronous (okay to call - // with sListeners lock held). - listener.onSensorChangedLocked(sensorObject, - values, timestamp, accuracy); - } - } - } - } - } - //Log.d(TAG, "exiting main sensor thread"); - } - } - } - - /*-----------------------------------------------------------------------*/ - - private class ListenerDelegate { - private final SensorEventListener mSensorEventListener; - private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>(); - private final Handler mHandler; - public SparseBooleanArray mSensors = new SparseBooleanArray(); - public SparseBooleanArray mFirstEvent = new SparseBooleanArray(); - public SparseIntArray mSensorAccuracies = new SparseIntArray(); - - ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) { - mSensorEventListener = listener; - Looper looper = (handler != null) ? handler.getLooper() : mMainLooper; - // currently we create one Handler instance per listener, but we could - // have one per looper (we'd need to pass the ListenerDelegate - // instance to handleMessage and keep track of them separately). - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - final SensorEvent t = (SensorEvent)msg.obj; - final int handle = t.sensor.getHandle(); - - switch (t.sensor.getType()) { - // Only report accuracy for sensors that support it. - case Sensor.TYPE_MAGNETIC_FIELD: - case Sensor.TYPE_ORIENTATION: - // call onAccuracyChanged() only if the value changes - final int accuracy = mSensorAccuracies.get(handle); - if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { - mSensorAccuracies.put(handle, t.accuracy); - mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); - } - break; - default: - // For other sensors, just report the accuracy once - if (mFirstEvent.get(handle) == false) { - mFirstEvent.put(handle, true); - mSensorEventListener.onAccuracyChanged( - t.sensor, SENSOR_STATUS_ACCURACY_HIGH); - } - break; - } - - mSensorEventListener.onSensorChanged(t); - sPool.returnToPool(t); - } - }; - addSensor(sensor); - } - - Object getListener() { - return mSensorEventListener; - } - - void addSensor(Sensor sensor) { - mSensors.put(sensor.getHandle(), true); - mSensorList.add(sensor); - } - int removeSensor(Sensor sensor) { - mSensors.delete(sensor.getHandle()); - mSensorList.remove(sensor); - return mSensors.size(); - } - boolean hasSensor(Sensor sensor) { - return mSensors.get(sensor.getHandle()); - } - List<Sensor> getSensors() { - return mSensorList; - } - - void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) { - SensorEvent t = sPool.getFromPool(); - final float[] v = t.values; - v[0] = values[0]; - v[1] = values[1]; - v[2] = values[2]; - t.timestamp = timestamp[0]; - t.accuracy = accuracy; - t.sensor = sensor; - Message msg = Message.obtain(); - msg.what = 0; - msg.obj = t; - msg.setAsynchronous(true); - mHandler.sendMessage(msg); - } - } - - /** - * {@hide} - */ + /** {@hide} */ public SystemSensorManager(Looper mainLooper) { mMainLooper = mainLooper; + mSensorEventQueueMap = new Hashtable<SensorEventListener, SensorEventQueue>(); - synchronized(sListeners) { + synchronized(sSensorModuleLock) { if (!sSensorModuleInitialized) { sSensorModuleInitialized = true; nativeClassInit(); // initialize the sensor list - sensors_module_init(); final ArrayList<Sensor> fullList = sFullSensorsList; int i = 0; do { Sensor sensor = new Sensor(); - i = sensors_module_get_next_sensor(sensor, i); - + i = nativeGetNextSensor(sensor, i); if (i>=0) { //Log.d(TAG, "found sensor: " + sensor.getName() + // ", handle=" + sensor.getHandle()); @@ -274,126 +82,304 @@ public class SystemSensorManager extends SensorManager { } while (i>0); sPool = new SensorEventPool( sFullSensorsList.size()*2 ); - sSensorThread = new SensorThread(); } } } + /** @hide */ @Override protected List<Sensor> getFullSensorList() { return sFullSensorsList; } - private boolean enableSensorLocked(Sensor sensor, int delay) { - boolean result = false; - for (ListenerDelegate i : sListeners) { - if (i.hasSensor(sensor)) { - String name = sensor.getName(); - int handle = sensor.getHandle(); - result = sensors_enable_sensor(sQueue, name, handle, delay); - break; + + /** @hide */ + @Override + protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, + int delay, Handler handler) + { + // Invariants to preserve: + // - one Looper per SensorEventListener + // - one Looper per SensorEventQueue + // We map SensorEventListeners to a SensorEventQueue, which holds the looper + + if (sensor == null) throw new NullPointerException("sensor cannot be null"); + + boolean result; + synchronized (mSensorEventQueueMap) { + // check if we already have this SensorEventListener, Sensor pair + // registered -- if so, we ignore the register. This is not ideal + // but this is what the implementation has always been doing. + for (SensorEventListenerSensorPair l : mListenerDelegates) { + if (l.isSameListenerSensorPair(listener, sensor)) { + // already added, just return silently. + return true; + } } - } - return result; - } - private boolean disableSensorLocked(Sensor sensor) { - for (ListenerDelegate i : sListeners) { - if (i.hasSensor(sensor)) { - // not an error, it's just that this sensor is still in use - return true; + // now find the SensorEventQueue associated to this listener + SensorEventQueue queue = mSensorEventQueueMap.get(listener); + if (queue != null) { + result = queue.addSensor(sensor, delay); + if (result) { + // create a new ListenerDelegate for this pair + mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor)); + } + } else { + Looper looper = (handler != null) ? handler.getLooper() : mMainLooper; + queue = new SensorEventQueue(listener, looper.getQueue()); + result = queue.addSensor(sensor, delay); + if (result) { + // create a new ListenerDelegate for this pair + mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor)); + mSensorEventQueueMap.put(listener, queue); + } else { + queue.dispose(); + } } } - String name = sensor.getName(); - int handle = sensor.getHandle(); - return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE); + return result; } /** @hide */ @Override - protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, - int delay, Handler handler) { - boolean result = true; - synchronized (sListeners) { - // look for this listener in our list - ListenerDelegate l = null; - for (ListenerDelegate i : sListeners) { - if (i.getListener() == listener) { - l = i; - break; + protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { + synchronized (mSensorEventQueueMap) { + + // remove this listener/sensor from our list + final ArrayList<SensorEventListenerSensorPair> copy = + new ArrayList<SensorEventListenerSensorPair>(mListenerDelegates); + int lastIndex = copy.size()-1; + for (int i=lastIndex ; i>= 0 ; i--) { + if (copy.get(i).isSameListenerSensorPair(listener, sensor)) { + mListenerDelegates.remove(i); } } - // if we don't find it, add it to the list - if (l == null) { - l = new ListenerDelegate(listener, sensor, handler); - sListeners.add(l); - // if the list is not empty, start our main thread - if (!sListeners.isEmpty()) { - if (sSensorThread.startLocked()) { - if (!enableSensorLocked(sensor, delay)) { - // oops. there was an error - sListeners.remove(l); - result = false; - } - } else { - // there was an error, remove the listener - sListeners.remove(l); - result = false; - } + // find the SensorEventQueue associated to this SensorEventListener + SensorEventQueue queue = mSensorEventQueueMap.get(listener); + if (queue != null) { + if (sensor != null) { + queue.removeSensor(sensor); } else { - // weird, we couldn't add the listener - result = false; + queue.removeAllSensors(); } - } else if (!l.hasSensor(sensor)) { - l.addSensor(sensor); - if (!enableSensorLocked(sensor, delay)) { - // oops. there was an error - l.removeSensor(sensor); - result = false; + if (!queue.hasSensors()) { + mSensorEventQueueMap.remove(listener); + queue.dispose(); } } } + } - return result; + + /* + * ListenerDelegate is essentially a SensorEventListener, Sensor pair + * and is associated with a single SensorEventQueue. + */ + private static final class SensorEventListenerSensorPair { + private final SensorEventListener mSensorEventListener; + private final Sensor mSensor; + public SensorEventListenerSensorPair(SensorEventListener listener, Sensor sensor) { + mSensorEventListener = listener; + mSensor = sensor; + } + public boolean isSameListenerSensorPair(SensorEventListener listener, Sensor sensor) { + // if sensor is null, we match only on the listener + if (sensor != null) { + return (listener == mSensorEventListener) && + (sensor.getHandle() == mSensor.getHandle()); + } else { + return (listener == mSensorEventListener); + } + } } - /** @hide */ - @Override - protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { - synchronized (sListeners) { - final int size = sListeners.size(); - for (int i=0 ; i<size ; i++) { - ListenerDelegate l = sListeners.get(i); - if (l.getListener() == listener) { - if (sensor == null) { - sListeners.remove(i); - // disable all sensors for this listener - for (Sensor s : l.getSensors()) { - disableSensorLocked(s); - } - // Check if the ListenerDelegate has the sensor it is trying to unregister. - } else if (l.hasSensor(sensor) && l.removeSensor(sensor) == 0) { - // if we have no more sensors enabled on this listener, - // take it off the list. - sListeners.remove(i); - disableSensorLocked(sensor); + /* + * SensorEventQueue is the communication channel with the sensor service, + * there is a one-to-one mapping between SensorEventQueue and + * SensorEventListener. + */ + private static final class SensorEventQueue { + private static native int nativeInitSensorEventQueue(SensorEventQueue eventQ, MessageQueue msgQ, float[] scratch); + private static native int nativeEnableSensor(int eventQ, int handle, int us); + private static native int nativeDisableSensor(int eventQ, int handle); + private static native void nativeDestroySensorEventQueue(int eventQ); + private int nSensorEventQueue; + private final SensorEventListener mListener; + private final SparseBooleanArray mActiveSensors = new SparseBooleanArray(); + private final SparseIntArray mSensorAccuracies = new SparseIntArray(); + private final SparseBooleanArray mFirstEvent = new SparseBooleanArray(); + private final CloseGuard mCloseGuard = CloseGuard.get(); + private final float[] mScratch = new float[16]; + + public SensorEventQueue(SensorEventListener listener, MessageQueue msgQ) { + nSensorEventQueue = nativeInitSensorEventQueue(this, msgQ, mScratch); + mListener = listener; + mCloseGuard.open("dispose"); + } + public void dispose() { + dispose(false); + } + + public boolean addSensor(Sensor sensor, int delay) { + if (enableSensor(sensor, delay) == 0) { + mActiveSensors.put(sensor.getHandle(), true); + return true; + } + return false; + } + + public void removeAllSensors() { + for (int i=0 ; i<mActiveSensors.size(); i++) { + if (mActiveSensors.valueAt(i) == true) { + int handle = mActiveSensors.keyAt(i); + Sensor sensor = sHandleToSensor.get(handle); + if (sensor != null) { + disableSensor(sensor); + mActiveSensors.put(handle, false); + } else { + // it should never happen -- just ignore. } - break; } } } + + public void removeSensor(Sensor sensor) { + final int handle = sensor.getHandle(); + if (mActiveSensors.get(handle)) { + disableSensor(sensor); + mActiveSensors.put(sensor.getHandle(), false); + } + } + + public boolean hasSensors() { + // no more sensors are set + return mActiveSensors.indexOfValue(true) >= 0; + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(true); + } finally { + super.finalize(); + } + } + + private void dispose(boolean finalized) { + if (mCloseGuard != null) { + if (finalized) { + mCloseGuard.warnIfOpen(); + } + mCloseGuard.close(); + } + if (nSensorEventQueue != 0) { + nativeDestroySensorEventQueue(nSensorEventQueue); + nSensorEventQueue = 0; + } + } + + private int enableSensor(Sensor sensor, int us) { + if (nSensorEventQueue == 0) throw new NullPointerException(); + if (sensor == null) throw new NullPointerException(); + return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), us); + } + private int disableSensor(Sensor sensor) { + if (nSensorEventQueue == 0) throw new NullPointerException(); + if (sensor == null) throw new NullPointerException(); + return nativeDisableSensor(nSensorEventQueue, sensor.getHandle()); + } + + // Called from native code. + @SuppressWarnings("unused") + private void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) { + // this is always called on the same thread. + final SensorEvent t = sPool.getFromPool(); + try { + final Sensor sensor = sHandleToSensor.get(handle); + final SensorEventListener listener = mListener; + // FIXME: handle more than 3 values + System.arraycopy(values, 0, t.values, 0, 3); + t.timestamp = timestamp; + t.accuracy = inAccuracy; + t.sensor = sensor; + switch (t.sensor.getType()) { + // Only report accuracy for sensors that support it. + case Sensor.TYPE_MAGNETIC_FIELD: + case Sensor.TYPE_ORIENTATION: + // call onAccuracyChanged() only if the value changes + final int accuracy = mSensorAccuracies.get(handle); + if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { + mSensorAccuracies.put(handle, t.accuracy); + listener.onAccuracyChanged(t.sensor, t.accuracy); + } + break; + default: + // For other sensors, just report the accuracy once + if (mFirstEvent.get(handle) == false) { + mFirstEvent.put(handle, true); + listener.onAccuracyChanged( + t.sensor, SENSOR_STATUS_ACCURACY_HIGH); + } + break; + } + listener.onSensorChanged(t); + } finally { + sPool.returnToPool(t); + } + } } - private static native void nativeClassInit(); + /* + * A dumb pool of SensorEvent + */ + private static final class SensorEventPool { + private final int mPoolSize; + private final SensorEvent mPool[]; + private int mNumItemsInPool; + + private SensorEvent createSensorEvent() { + // maximal size for all legacy events is 3 + return new SensorEvent(3); + } - private static native int sensors_module_init(); - private static native int sensors_module_get_next_sensor(Sensor sensor, int next); + SensorEventPool(int poolSize) { + mPoolSize = poolSize; + mNumItemsInPool = poolSize; + mPool = new SensorEvent[poolSize]; + } - // Used within this module from outside SensorManager, don't make private - static native int sensors_create_queue(); - static native void sensors_destroy_queue(int queue); - static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable); - static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp); + SensorEvent getFromPool() { + SensorEvent t = null; + synchronized (this) { + if (mNumItemsInPool > 0) { + // remove the "top" item from the pool + final int index = mPoolSize - mNumItemsInPool; + t = mPool[index]; + mPool[index] = null; + mNumItemsInPool--; + } + } + if (t == null) { + // the pool was empty or this item was removed from the pool for + // the first time. In any case, we need to create a new item. + t = createSensorEvent(); + } + return t; + } + + void returnToPool(SensorEvent t) { + synchronized (this) { + // is there space left in the pool? + if (mNumItemsInPool < mPoolSize) { + // if so, return the item to the pool + mNumItemsInPool++; + final int index = mPoolSize - mNumItemsInPool; + mPool[index] = t; + } + } + } + } } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 262d87d..761faaf 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -212,8 +212,10 @@ public final class InputManager { } catch (RemoteException ex) { throw new RuntimeException("Could not get input device information.", ex); } + if (inputDevice != null) { + mInputDevices.setValueAt(index, inputDevice); + } } - mInputDevices.setValueAt(index, inputDevice); return inputDevice; } } @@ -241,6 +243,8 @@ public final class InputManager { inputDevice = mIm.getInputDevice(id); } catch (RemoteException ex) { // Ignore the problem for the purposes of this method. + } + if (inputDevice == null) { continue; } mInputDevices.setValueAt(i, inputDevice); @@ -809,6 +813,22 @@ public final class InputManager { } } + /** + * @hide + */ + @Override + public void vibrate(int owningUid, String owningPackage, long milliseconds) { + vibrate(milliseconds); + } + + /** + * @hide + */ + @Override + public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) { + vibrate(pattern, repeat); + } + @Override public void cancel() { try { diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 8286686..9bc967f 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -95,4 +95,7 @@ interface IUsbManager /* Deny USB debugging from the attached host */ void denyUsbDebugging(); + + /* Clear public keys installed for secure USB debugging */ + void clearUsbDebuggingKeys(); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6f1cc94..99624cc 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1955,7 +1955,7 @@ public class InputMethodService extends AbstractInputMethodService { ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); - ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); } diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java index ce71e6b..21995c0 100644 --- a/core/java/android/net/CaptivePortalTracker.java +++ b/core/java/android/net/CaptivePortalTracker.java @@ -25,14 +25,15 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.IConnectivityManager; +import android.os.Handler; import android.os.UserHandle; import android.os.Message; import android.os.RemoteException; import android.provider.Settings; import android.telephony.TelephonyManager; -import android.util.Log; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -81,15 +82,21 @@ public class CaptivePortalTracker extends StateMachine { private State mActiveNetworkState = new ActiveNetworkState(); private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState(); + private static final String SETUP_WIZARD_PACKAGE = "com.google.android.setupwizard"; + private boolean mDeviceProvisioned = false; + private ProvisioningObserver mProvisioningObserver; + private CaptivePortalTracker(Context context, IConnectivityManager cs) { super(TAG); mContext = context; mConnService = cs; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + mProvisioningObserver = new ProvisioningObserver(); IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE); mContext.registerReceiver(mReceiver, filter); mServer = Settings.Global.getString(mContext.getContentResolver(), @@ -106,11 +113,31 @@ public class CaptivePortalTracker extends StateMachine { setInitialState(mNoActiveNetworkState); } + private class ProvisioningObserver extends ContentObserver { + ProvisioningObserver() { + super(new Handler()); + mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( + Settings.Global.DEVICE_PROVISIONED), false, this); + onChange(false); // load initial value + } + + @Override + public void onChange(boolean selfChange) { + mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) != 0; + } + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + // Normally, we respond to CONNECTIVITY_ACTION, allowing time for the change in + // connectivity to stabilize, but if the device is not yet provisioned, respond + // immediately to speed up transit through the setup wizard. + if ((mDeviceProvisioned && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) + || (!mDeviceProvisioned + && action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE))) { NetworkInfo info = intent.getParcelableExtra( ConnectivityManager.EXTRA_NETWORK_INFO); sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info)); @@ -222,8 +249,12 @@ public class CaptivePortalTracker extends StateMachine { @Override public void enter() { if (DBG) log(getName() + "\n"); - sendMessageDelayed(obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, - ++mDelayedCheckToken, 0), DELAYED_CHECK_INTERVAL_MS); + Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0); + if (mDeviceProvisioned) { + sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS); + } else { + sendMessage(message); + } } @Override @@ -233,13 +264,26 @@ public class CaptivePortalTracker extends StateMachine { case CMD_DELAYED_CAPTIVE_CHECK: if (message.arg1 == mDelayedCheckToken) { InetAddress server = lookupHost(mServer); - if (server != null) { - if (isCaptivePortal(server)) { - if (DBG) log("Captive network " + mNetworkInfo); + boolean captive = server != null && isCaptivePortal(server); + if (captive) { + if (DBG) log("Captive network " + mNetworkInfo); + } else { + if (DBG) log("Not captive network " + mNetworkInfo); + } + if (mDeviceProvisioned) { + if (captive) { + // Setup Wizard will assist the user in connecting to a captive + // portal, so make the notification visible unless during setup setNotificationVisible(true); } + } else { + Intent intent = new Intent( + ConnectivityManager.ACTION_CAPTIVE_PORTAL_TEST_COMPLETED); + intent.putExtra(ConnectivityManager.EXTRA_IS_CAPTIVE_PORTAL, captive); + intent.setPackage(SETUP_WIZARD_PACKAGE); + mContext.sendBroadcast(intent); } - if (DBG) log("Not captive network " + mNetworkInfo); + transitionTo(mActiveNetworkState); } break; @@ -370,13 +414,4 @@ public class CaptivePortalTracker extends StateMachine { } mNotificationShown = visible; } - - private static void log(String s) { - Log.d(TAG, s); - } - - private static void loge(String s) { - Log.e(TAG, s); - } - } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 6ff1a33..1f44e49 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -70,6 +70,7 @@ public class ConnectivityManager { * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY * is set to {@code true} if there are no connected networks at all. */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; /** @@ -78,6 +79,7 @@ public class ConnectivityManager { * * @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String CONNECTIVITY_ACTION_IMMEDIATE = "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE"; @@ -198,6 +200,7 @@ public class ConnectivityManager { * the network and it's condition. * @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String INET_CONDITION_ACTION = "android.net.conn.INET_CONDITION_ACTION"; @@ -206,6 +209,7 @@ public class ConnectivityManager { * TODO - finish the doc * @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; @@ -228,6 +232,22 @@ public class ConnectivityManager { public static final String EXTRA_ERRORED_TETHER = "erroredArray"; /** + * Broadcast Action: The captive portal tracker has finished its test. + * Sent only while running Setup Wizard, in lieu of showing a user + * notification. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CAPTIVE_PORTAL_TEST_COMPLETED = + "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED"; + /** + * The lookup key for a boolean that indicates whether a captive portal was detected. + * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}. + * @hide + */ + public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal"; + + /** * The absence of APN.. * @hide */ @@ -328,6 +348,18 @@ public class ConnectivityManager { /** {@hide} */ public static final int MAX_NETWORK_TYPE = TYPE_WIFI_P2P; + /** + * If you want to set the default network preference,you can directly + * change the networkAttributes array in framework's config.xml. + * + * @deprecated Since we support so many more networks now, the single + * network default network preference can't really express + * the heirarchy. Instead, the default is defined by the + * networkAttributes in config.xml. You can determine + * the current value by calling {@link #getNetworkPreference()} + * from an App. + */ + @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI; /** diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java index e2660e4..2b359eb 100644 --- a/core/java/android/net/DhcpInfo.java +++ b/core/java/android/net/DhcpInfo.java @@ -22,16 +22,17 @@ import java.net.InetAddress; /** * A simple object for retrieving the results of a DHCP request. + * @deprecated - use LinkProperties - To be removed 11/2013 + * STOPSHIP - make sure we expose LinkProperties through ConnectivityManager */ public class DhcpInfo implements Parcelable { public int ipAddress; public int gateway; public int netmask; - public int dns1; public int dns2; - public int serverAddress; + public int leaseDuration; public DhcpInfo() { diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java deleted file mode 100644 index f3508c1..0000000 --- a/core/java/android/net/DhcpInfoInternal.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.text.TextUtils; -import android.util.Log; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -/** - * A simple object for retrieving the results of a DHCP request. - * Replaces (internally) the IPv4-only DhcpInfo class. - * @hide - */ -public class DhcpInfoInternal { - private final static String TAG = "DhcpInfoInternal"; - public String ipAddress; - public int prefixLength; - - public String dns1; - public String dns2; - - public String serverAddress; - public int leaseDuration; - - /** - * Vendor specific information (from RFC 2132). - */ - public String vendorInfo; - - private Collection<RouteInfo> mRoutes; - - public DhcpInfoInternal() { - mRoutes = new ArrayList<RouteInfo>(); - } - - public void addRoute(RouteInfo routeInfo) { - mRoutes.add(routeInfo); - } - - public Collection<RouteInfo> getRoutes() { - return Collections.unmodifiableCollection(mRoutes); - } - - private int convertToInt(String addr) { - if (addr != null) { - try { - InetAddress inetAddress = NetworkUtils.numericToInetAddress(addr); - if (inetAddress instanceof Inet4Address) { - return NetworkUtils.inetAddressToInt(inetAddress); - } - } catch (IllegalArgumentException e) {} - } - return 0; - } - - public DhcpInfo makeDhcpInfo() { - DhcpInfo info = new DhcpInfo(); - info.ipAddress = convertToInt(ipAddress); - for (RouteInfo route : mRoutes) { - if (route.isDefaultRoute()) { - info.gateway = convertToInt(route.getGateway().getHostAddress()); - break; - } - } - try { - InetAddress inetAddress = NetworkUtils.numericToInetAddress(ipAddress); - info.netmask = NetworkUtils.prefixLengthToNetmaskInt(prefixLength); - } catch (IllegalArgumentException e) {} - info.dns1 = convertToInt(dns1); - info.dns2 = convertToInt(dns2); - info.serverAddress = convertToInt(serverAddress); - info.leaseDuration = leaseDuration; - return info; - } - - public LinkAddress makeLinkAddress() { - if (TextUtils.isEmpty(ipAddress)) { - Log.e(TAG, "makeLinkAddress with empty ipAddress"); - return null; - } - return new LinkAddress(NetworkUtils.numericToInetAddress(ipAddress), prefixLength); - } - - public LinkProperties makeLinkProperties() { - LinkProperties p = new LinkProperties(); - p.addLinkAddress(makeLinkAddress()); - for (RouteInfo route : mRoutes) { - p.addRoute(route); - } - //if empty, connectivity configures default DNS - if (TextUtils.isEmpty(dns1) == false) { - p.addDns(NetworkUtils.numericToInetAddress(dns1)); - } else { - Log.d(TAG, "makeLinkProperties with empty dns1!"); - } - if (TextUtils.isEmpty(dns2) == false) { - p.addDns(NetworkUtils.numericToInetAddress(dns2)); - } else { - Log.d(TAG, "makeLinkProperties with empty dns2!"); - } - return p; - } - - /* Updates the DHCP fields that need to be retained from - * original DHCP request if the DHCP renewal shows them as - * being empty - */ - public void updateFromDhcpRequest(DhcpInfoInternal orig) { - if (orig == null) return; - - if (TextUtils.isEmpty(dns1)) { - dns1 = orig.dns1; - } - - if (TextUtils.isEmpty(dns2)) { - dns2 = orig.dns2; - } - - if (mRoutes.size() == 0) { - for (RouteInfo route : orig.getRoutes()) { - addRoute(route); - } - } - } - - /** - * Test if this DHCP lease includes vendor hint that network link is - * metered, and sensitive to heavy data transfers. - */ - public boolean hasMeteredHint() { - if (vendorInfo != null) { - return vendorInfo.contains("ANDROID_METERED"); - } else { - return false; - } - } - - public String toString() { - String routeString = ""; - for (RouteInfo route : mRoutes) routeString += route.toString() + " | "; - return "addr: " + ipAddress + "/" + prefixLength + - " mRoutes: " + routeString + - " dns: " + dns1 + "," + dns2 + - " dhcpServer: " + serverAddress + - " leaseDuration: " + leaseDuration; - } -} diff --git a/core/java/android/util/PoolableManager.java b/core/java/android/net/DhcpResults.aidl index 8773e63..f4db3c3 100644 --- a/core/java/android/util/PoolableManager.java +++ b/core/java/android/net/DhcpResults.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2009 The Android Open Source Project +/** + * Copyright (c) 2012, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,14 +14,6 @@ * limitations under the License. */ -package android.util; - -/** - * @hide - */ -public interface PoolableManager<T extends Poolable<T>> { - T newInstance(); +package android.net; - void onAcquired(T element); - void onReleased(T element); -} +parcelable DhcpResults; diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java new file mode 100644 index 0000000..a3f70da --- /dev/null +++ b/core/java/android/net/DhcpResults.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.os.Parcelable; +import android.os.Parcel; +import android.text.TextUtils; +import android.util.Log; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * A simple object for retrieving the results of a DHCP request. + * Optimized (attempted) for that jni interface + * TODO - remove when DhcpInfo is deprecated. Move the remaining api to LinkProperties. + * @hide + */ +public class DhcpResults implements Parcelable { + private static final String TAG = "DhcpResults"; + + public final LinkProperties linkProperties; + + public InetAddress serverAddress; + + /** + * Vendor specific information (from RFC 2132). + */ + public String vendorInfo; + + public int leaseDuration; + + public DhcpResults() { + linkProperties = new LinkProperties(); + } + + /** copy constructor */ + public DhcpResults(DhcpResults source) { + if (source != null) { + linkProperties = new LinkProperties(source.linkProperties); + serverAddress = source.serverAddress; + leaseDuration = source.leaseDuration; + vendorInfo = source.vendorInfo; + } else { + linkProperties = new LinkProperties(); + } + } + + public DhcpResults(LinkProperties lp) { + linkProperties = new LinkProperties(lp); + } + + /** + * Updates the DHCP fields that need to be retained from + * original DHCP request if the current renewal shows them + * being empty. + */ + public void updateFromDhcpRequest(DhcpResults orig) { + if (orig == null || orig.linkProperties == null) return; + if (linkProperties.getRoutes().size() == 0) { + for (RouteInfo r : orig.linkProperties.getRoutes()) linkProperties.addRoute(r); + } + if (linkProperties.getDnses().size() == 0) { + for (InetAddress d : orig.linkProperties.getDnses()) linkProperties.addDns(d); + } + } + + /** + * Test if this DHCP lease includes vendor hint that network link is + * metered, and sensitive to heavy data transfers. + */ + public boolean hasMeteredHint() { + if (vendorInfo != null) { + return vendorInfo.contains("ANDROID_METERED"); + } else { + return false; + } + } + + public void clear() { + linkProperties.clear(); + serverAddress = null; + vendorInfo = null; + leaseDuration = 0; + } + + @Override + public String toString() { + StringBuffer str = new StringBuffer(linkProperties.toString()); + + str.append(" DHCP server ").append(serverAddress); + str.append(" Vendor info ").append(vendorInfo); + str.append(" lease ").append(leaseDuration).append(" seconds"); + + return str.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof DhcpResults)) return false; + + DhcpResults target = (DhcpResults)obj; + + if (linkProperties == null) { + if (target.linkProperties != null) return false; + } else if (!linkProperties.equals(target.linkProperties)) return false; + if (serverAddress == null) { + if (target.serverAddress != null) return false; + } else if (!serverAddress.equals(target.serverAddress)) return false; + if (vendorInfo == null) { + if (target.vendorInfo != null) return false; + } else if (!vendorInfo.equals(target.vendorInfo)) return false; + if (leaseDuration != target.leaseDuration) return false; + + return true; + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public void writeToParcel(Parcel dest, int flags) { + linkProperties.writeToParcel(dest, flags); + + dest.writeInt(leaseDuration); + + if (serverAddress != null) { + dest.writeByte((byte)1); + dest.writeByteArray(serverAddress.getAddress()); + } else { + dest.writeByte((byte)0); + } + + dest.writeString(vendorInfo); + } + + /** Implement the Parcelable interface */ + public static final Creator<DhcpResults> CREATOR = + new Creator<DhcpResults>() { + public DhcpResults createFromParcel(Parcel in) { + DhcpResults prop = new DhcpResults((LinkProperties)in.readParcelable(null)); + + prop.leaseDuration = in.readInt(); + + if (in.readByte() == 1) { + try { + prop.serverAddress = InetAddress.getByAddress(in.createByteArray()); + } catch (UnknownHostException e) {} + } + + prop.vendorInfo = in.readString(); + + return prop; + } + + public DhcpResults[] newArray(int size) { + return new DhcpResults[size]; + } + }; + + // Utils for jni population - false on success + public void setInterfaceName(String interfaceName) { + linkProperties.setInterfaceName(interfaceName); + } + + public boolean addLinkAddress(String addrString, int prefixLength) { + InetAddress addr; + try { + addr = NetworkUtils.numericToInetAddress(addrString); + } catch (IllegalArgumentException e) { + Log.e(TAG, "addLinkAddress failed with addrString " + addrString); + return true; + } + + LinkAddress linkAddress = new LinkAddress(addr, prefixLength); + linkProperties.addLinkAddress(linkAddress); + + RouteInfo routeInfo = new RouteInfo(linkAddress); + linkProperties.addRoute(routeInfo); + return false; + } + + public boolean addGateway(String addrString) { + try { + linkProperties.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(addrString))); + } catch (IllegalArgumentException e) { + Log.e(TAG, "addGateway failed with addrString " + addrString); + return true; + } + return false; + } + + public boolean addDns(String addrString) { + if (TextUtils.isEmpty(addrString) == false) { + try { + linkProperties.addDns(NetworkUtils.numericToInetAddress(addrString)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "addDns failed with addrString " + addrString); + return true; + } + } + return false; + } + + public boolean setServerAddress(String addrString) { + try { + serverAddress = NetworkUtils.numericToInetAddress(addrString); + } catch (IllegalArgumentException e) { + Log.e(TAG, "setServerAddress failed with addrString " + addrString); + return true; + } + return false; + } + + public void setLeaseDuration(int duration) { + leaseDuration = duration; + } + + public void setVendorInfo(String info) { + vendorInfo = info; + } + + public void setDomains(String domains) { + linkProperties.setDomains(domains); + } +} diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java index 8dc900e..fd22b10 100644 --- a/core/java/android/net/DhcpStateMachine.java +++ b/core/java/android/net/DhcpStateMachine.java @@ -26,7 +26,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.net.DhcpInfoInternal; +import android.net.DhcpResults; import android.net.NetworkUtils; import android.os.Message; import android.os.PowerManager; @@ -64,7 +64,7 @@ public class DhcpStateMachine extends StateMachine { private static final String WAKELOCK_TAG = "DHCP"; //Remember DHCP configuration from first request - private DhcpInfoInternal mDhcpInfo; + private DhcpResults mDhcpResults; private static final int DHCP_RENEW = 0; private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW"; @@ -348,23 +348,21 @@ public class DhcpStateMachine extends StateMachine { private boolean runDhcp(DhcpAction dhcpAction) { boolean success = false; - DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); + DhcpResults dhcpResults = new DhcpResults(); if (dhcpAction == DhcpAction.START) { /* Stop any existing DHCP daemon before starting new */ NetworkUtils.stopDhcp(mInterfaceName); if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName); - success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal); - mDhcpInfo = dhcpInfoInternal; + success = NetworkUtils.runDhcp(mInterfaceName, dhcpResults); } else if (dhcpAction == DhcpAction.RENEW) { if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName); - success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal); - dhcpInfoInternal.updateFromDhcpRequest(mDhcpInfo); + success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpResults); + dhcpResults.updateFromDhcpRequest(mDhcpResults); } - if (success) { if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName); - long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion + long leaseDuration = dhcpResults.leaseDuration; //int to long conversion //Sanity check for renewal if (leaseDuration >= 0) { @@ -384,7 +382,8 @@ public class DhcpStateMachine extends StateMachine { //infinite lease time, no renewal needed } - mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal) + mDhcpResults = dhcpResults; + mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults) .sendToTarget(); } else { Log.e(TAG, "DHCP failed on " + mInterfaceName + ": " + diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 37601fc..8947162 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -170,13 +170,12 @@ public class EthernetDataTracker implements NetworkStateTracker { private void runDhcp() { Thread dhcpThread = new Thread(new Runnable() { public void run() { - DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); - if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) { + DhcpResults dhcpResults = new DhcpResults(); + if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } - mLinkProperties = dhcpInfoInternal.makeLinkProperties(); - mLinkProperties.setInterfaceName(mIface); + mLinkProperties = dhcpResults.linkProperties; mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 75646fd..b9362da 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -51,9 +51,10 @@ import java.util.Collections; */ public class LinkProperties implements Parcelable { - String mIfaceName; + private String mIfaceName; private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>(); private Collection<InetAddress> mDnses = new ArrayList<InetAddress>(); + private String mDomains; private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); private ProxyProperties mHttpProxy; @@ -82,9 +83,10 @@ public class LinkProperties implements Parcelable { mIfaceName = source.getInterfaceName(); for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l); for (InetAddress i : source.getDnses()) mDnses.add(i); + mDomains = source.getDomains(); for (RouteInfo r : source.getRoutes()) mRoutes.add(r); mHttpProxy = (source.getHttpProxy() == null) ? - null : new ProxyProperties(source.getHttpProxy()); + null : new ProxyProperties(source.getHttpProxy()); } } @@ -120,6 +122,14 @@ public class LinkProperties implements Parcelable { return Collections.unmodifiableCollection(mDnses); } + public String getDomains() { + return mDomains; + } + + public void setDomains(String domains) { + mDomains = domains; + } + public void addRoute(RouteInfo route) { if (route != null) mRoutes.add(route); } @@ -138,6 +148,7 @@ public class LinkProperties implements Parcelable { mIfaceName = null; mLinkAddresses.clear(); mDnses.clear(); + mDomains = null; mRoutes.clear(); mHttpProxy = null; } @@ -162,12 +173,14 @@ public class LinkProperties implements Parcelable { for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; dns += "] "; - String routes = "Routes: ["; + String domainName = "Domains: " + mDomains; + + String routes = " Routes: ["; for (RouteInfo route : mRoutes) routes += route.toString() + ","; routes += "] "; String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " "); - return ifaceName + linkAddresses + routes + dns + proxy; + return ifaceName + linkAddresses + routes + dns + domainName + proxy; } /** @@ -181,7 +194,7 @@ public class LinkProperties implements Parcelable { } /** - * Compares this {@code LinkProperties} interface name against the target + * Compares this {@code LinkProperties} interface addresses against the target * * @param target LinkProperties to compare. * @return {@code true} if both are identical, {@code false} otherwise. @@ -201,6 +214,12 @@ public class LinkProperties implements Parcelable { */ public boolean isIdenticalDnses(LinkProperties target) { Collection<InetAddress> targetDnses = target.getDnses(); + String targetDomains = target.getDomains(); + if (mDomains == null) { + if (targetDomains != null) return false; + } else { + if (mDomains.equals(targetDomains) == false) return false; + } return (mDnses.size() == targetDnses.size()) ? mDnses.containsAll(targetDnses) : false; } @@ -359,13 +378,13 @@ public class LinkProperties implements Parcelable { return ((null == mIfaceName) ? 0 : mIfaceName.hashCode() + mLinkAddresses.size() * 31 + mDnses.size() * 37 + + ((null == mDomains) ? 0 : mDomains.hashCode()) + mRoutes.size() * 41 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())); } /** * Implement the Parcelable interface. - * @hide */ public void writeToParcel(Parcel dest, int flags) { dest.writeString(getInterfaceName()); @@ -378,6 +397,7 @@ public class LinkProperties implements Parcelable { for(InetAddress d : mDnses) { dest.writeByteArray(d.getAddress()); } + dest.writeString(mDomains); dest.writeInt(mRoutes.size()); for(RouteInfo route : mRoutes) { @@ -394,19 +414,15 @@ public class LinkProperties implements Parcelable { /** * Implement the Parcelable interface. - * @hide */ public static final Creator<LinkProperties> CREATOR = new Creator<LinkProperties>() { public LinkProperties createFromParcel(Parcel in) { LinkProperties netProp = new LinkProperties(); + String iface = in.readString(); if (iface != null) { - try { - netProp.setInterfaceName(iface); - } catch (Exception e) { - return null; - } + netProp.setInterfaceName(iface); } int addressCount = in.readInt(); for (int i=0; i<addressCount; i++) { @@ -418,6 +434,7 @@ public class LinkProperties implements Parcelable { netProp.addDns(InetAddress.getByAddress(in.createByteArray())); } catch (UnknownHostException e) { } } + netProp.setDomains(in.readString()); addressCount = in.readInt(); for (int i=0; i<addressCount; i++) { netProp.addRoute((RouteInfo)in.readParcelable(null)); diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 0b23cb7..689dae5 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -19,6 +19,8 @@ package android.net; import android.os.Parcelable; import android.os.Parcel; +import com.android.internal.annotations.VisibleForTesting; + import java.util.EnumMap; /** @@ -312,7 +314,9 @@ public class NetworkInfo implements Parcelable { } } - void setRoaming(boolean isRoaming) { + /** {@hide} */ + @VisibleForTesting + public void setRoaming(boolean isRoaming) { synchronized (this) { mIsRoaming = isRoaming; } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index c757605..9cb904d 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -135,6 +135,18 @@ public class NetworkStats implements Parcelable { builder.append(" operations=").append(operations); return builder.toString(); } + + @Override + public boolean equals(Object o) { + if (o instanceof Entry) { + final Entry e = (Entry) o; + return uid == e.uid && set == e.set && tag == e.tag && rxBytes == e.rxBytes + && rxPackets == e.rxPackets && txBytes == e.txBytes + && txPackets == e.txPackets && operations == e.operations + && iface.equals(e.iface); + } + return false; + } } public NetworkStats(long elapsedRealtime, int initialSize) { diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index d3839ad..c189ba4 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -22,6 +22,7 @@ import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED; import static android.net.NetworkIdentity.scrubSubscriberId; +import static android.net.wifi.WifiInfo.removeDoubleQuotes; import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; @@ -279,7 +280,8 @@ public class NetworkTemplate implements Parcelable { private boolean matchesWifi(NetworkIdentity ident) { switch (ident.mType) { case TYPE_WIFI: - return Objects.equal(mNetworkId, ident.mNetworkId); + return Objects.equal( + removeDoubleQuotes(mNetworkId), removeDoubleQuotes(ident.mNetworkId)); default: return false; } diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index d39e741..4ab479e 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -62,21 +62,21 @@ public class NetworkUtils { * addresses. This call blocks until it obtains a result (either success * or failure) from the daemon. * @param interfaceName the name of the interface to configure - * @param ipInfo if the request succeeds, this object is filled in with + * @param dhcpResults if the request succeeds, this object is filled in with * the IP address information. * @return {@code true} for success, {@code false} for failure */ - public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo); + public native static boolean runDhcp(String interfaceName, DhcpResults dhcpResults); /** * Initiate renewal on the Dhcp client daemon. This call blocks until it obtains * a result (either success or failure) from the daemon. * @param interfaceName the name of the interface to configure - * @param ipInfo if the request succeeds, this object is filled in with + * @param dhcpResults if the request succeeds, this object is filled in with * the IP address information. * @return {@code true} for success, {@code false} for failure */ - public native static boolean runDhcpRenew(String interfaceName, DhcpInfoInternal ipInfo); + public native static boolean runDhcpRenew(String interfaceName, DhcpResults dhcpResults); /** * Shut down the DHCP client daemon. @@ -124,12 +124,9 @@ public class NetworkUtils { * @param inetAddr is an InetAddress corresponding to the IPv4 address * @return the IP address as an integer in network byte order */ - public static int inetAddressToInt(InetAddress inetAddr) + public static int inetAddressToInt(Inet4Address inetAddr) throws IllegalArgumentException { byte [] addr = inetAddr.getAddress(); - if (addr.length != 4) { - throw new IllegalArgumentException("Not an IPv4 address"); - } return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) | ((addr[1] & 0xff) << 8) | (addr[0] & 0xff); } diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index 275f32a..112e143 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -76,6 +76,10 @@ public class RouteInfo implements Parcelable { this(null, gateway); } + public RouteInfo(LinkAddress host) { + this(host, null); + } + public static RouteInfo makeHostRoute(InetAddress host) { return makeHostRoute(host, null); } diff --git a/core/java/android/net/ThrottleManager.java b/core/java/android/net/ThrottleManager.java deleted file mode 100644 index 5fdac58..0000000 --- a/core/java/android/net/ThrottleManager.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.os.Binder; -import android.os.RemoteException; - -/** - * Class that handles throttling. It provides read/write numbers per interface - * and methods to apply throttled rates. - * {@hide} - */ -public class ThrottleManager -{ - /** - * Broadcast each polling period to indicate new data counts. - * - * Includes four extras: - * EXTRA_CYCLE_READ - a long of the read bytecount for the current cycle - * EXTRA_CYCLE_WRITE -a long of the write bytecount for the current cycle - * EXTRA_CYLCE_START -a long of MS for the cycle start time - * EXTRA_CYCLE_END -a long of MS for the cycle stop time - * {@hide} - */ - public static final String THROTTLE_POLL_ACTION = "android.net.thrott.POLL_ACTION"; - /** - * The lookup key for a long for the read bytecount for this period. Retrieve with - * {@link android.content.Intent#getLongExtra(String)}. - * {@hide} - */ - public static final String EXTRA_CYCLE_READ = "cycleRead"; - /** - * contains a long of the number of bytes written in the cycle - * {@hide} - */ - public static final String EXTRA_CYCLE_WRITE = "cycleWrite"; - /** - * contains a long of the number of bytes read in the cycle - * {@hide} - */ - public static final String EXTRA_CYCLE_START = "cycleStart"; - /** - * contains a long of the ms since 1970 used to init a calendar, etc for the end - * of the cycle - * {@hide} - */ - public static final String EXTRA_CYCLE_END = "cycleEnd"; - - /** - * Broadcast when the thottle level changes. - * {@hide} - */ - public static final String THROTTLE_ACTION = "android.net.thrott.THROTTLE_ACTION"; - /** - * int of the current bandwidth in TODO - * {@hide} - */ - public static final String EXTRA_THROTTLE_LEVEL = "level"; - - /** - * Broadcast on boot and whenever the settings change. - * {@hide} - */ - public static final String POLICY_CHANGED_ACTION = "android.net.thrott.POLICY_CHANGED_ACTION"; - - // {@hide} - public static final int DIRECTION_TX = 0; - // {@hide} - public static final int DIRECTION_RX = 1; - - // {@hide} - public static final int PERIOD_CYCLE = 0; - // {@hide} - public static final int PERIOD_YEAR = 1; - // {@hide} - public static final int PERIOD_MONTH = 2; - // {@hide} - public static final int PERIOD_WEEK = 3; - // @hide - public static final int PERIOD_7DAY = 4; - // @hide - public static final int PERIOD_DAY = 5; - // @hide - public static final int PERIOD_24HOUR = 6; - // @hide - public static final int PERIOD_HOUR = 7; - // @hide - public static final int PERIOD_60MIN = 8; - // @hide - public static final int PERIOD_MINUTE = 9; - // @hide - public static final int PERIOD_60SEC = 10; - // @hide - public static final int PERIOD_SECOND = 11; - - - - /** - * returns a long of the ms from the epoch to the time the current cycle ends for the - * named interface - * {@hide} - */ - public long getResetTime(String iface) { - try { - return mService.getResetTime(iface); - } catch (RemoteException e) { - return -1; - } - } - - /** - * returns a long of the ms from the epoch to the time the current cycle started for the - * named interface - * {@hide} - */ - public long getPeriodStartTime(String iface) { - try { - return mService.getPeriodStartTime(iface); - } catch (RemoteException e) { - return -1; - } - } - - /** - * returns a long of the byte count either read or written on the named interface - * for the period described. Direction is either DIRECTION_RX or DIRECTION_TX and - * period may only be PERIOD_CYCLE for the current cycle (other periods may be supported - * in the future). Ago indicates the number of periods in the past to lookup - 0 means - * the current period, 1 is the last one, 2 was two periods ago.. - * {@hide} - */ - public long getByteCount(String iface, int direction, int period, int ago) { - try { - return mService.getByteCount(iface, direction, period, ago); - } catch (RemoteException e) { - return -1; - } - } - - /** - * returns the number of bytes read+written after which a particular cliff - * takes effect on the named iface. Currently only cliff #1 is supported (1 step) - * {@hide} - */ - public long getCliffThreshold(String iface, int cliff) { - try { - return mService.getCliffThreshold(iface, cliff); - } catch (RemoteException e) { - return -1; - } - } - - /** - * returns the thottling bandwidth (bps) for a given cliff # on the named iface. - * only cliff #1 is currently supported. - * {@hide} - */ - public int getCliffLevel(String iface, int cliff) { - try { - return mService.getCliffLevel(iface, cliff); - } catch (RemoteException e) { - return -1; - } - } - - /** - * returns the help URI for throttling - * {@hide} - */ - public String getHelpUri() { - try { - return mService.getHelpUri(); - } catch (RemoteException e) { - return null; - } - } - - - private IThrottleManager mService; - - /** - * Don't allow use of default constructor. - */ - @SuppressWarnings({"UnusedDeclaration"}) - private ThrottleManager() { - } - - /** - * {@hide} - */ - public ThrottleManager(IThrottleManager service) { - if (service == null) { - throw new IllegalArgumentException( - "ThrottleManager() cannot be constructed with null service"); - } - mService = service; - } -} diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index e437d2e..a282910 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -315,6 +315,30 @@ public class TrafficStats { return total; } + /** {@hide} */ + public static long getMobileTcpRxPackets() { + long total = 0; + for (String iface : getMobileIfaces()) { + final long stat = nativeGetIfaceStat(iface, TYPE_TCP_RX_PACKETS); + if (stat != UNSUPPORTED) { + total += stat; + } + } + return total; + } + + /** {@hide} */ + public static long getMobileTcpTxPackets() { + long total = 0; + for (String iface : getMobileIfaces()) { + final long stat = nativeGetIfaceStat(iface, TYPE_TCP_TX_PACKETS); + if (stat != UNSUPPORTED) { + total += stat; + } + } + return total; + } + /** * Get the total number of packets transmitted through the specified interface. * @@ -400,161 +424,156 @@ public class TrafficStats { } /** - * Get the number of bytes sent through the network for this UID. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. + * Return number of bytes transmitted by the given UID since device boot. + * Counts packets across all network interfaces, and always increases + * monotonically since device boot. Statistics are measured at the network + * layer, so they include both TCP and UDP usage. + * <p> + * Before {@link android.os.Build.VERSION_CODES#K}, this may return + * {@link #UNSUPPORTED} on devices where statistics aren't available. * - * @param uid The UID of the process to examine. - * @return number of bytes. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @see android.os.Process#myUid() + * @see android.content.pm.ApplicationInfo#uid */ - public static native long getUidTxBytes(int uid); + public static long getUidTxBytes(int uid) { + return nativeGetUidStat(uid, TYPE_TX_BYTES); + } /** - * Get the number of bytes received through the network for this UID. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. + * Return number of bytes received by the given UID since device boot. + * Counts packets across all network interfaces, and always increases + * monotonically since device boot. Statistics are measured at the network + * layer, so they include both TCP and UDP usage. + * <p> + * Before {@link android.os.Build.VERSION_CODES#K}, this may return + * {@link #UNSUPPORTED} on devices where statistics aren't available. * - * @param uid The UID of the process to examine. - * @return number of bytes + * @see android.os.Process#myUid() + * @see android.content.pm.ApplicationInfo#uid */ - public static native long getUidRxBytes(int uid); + public static long getUidRxBytes(int uid) { + return nativeGetUidStat(uid, TYPE_RX_BYTES); + } /** - * Get the number of packets (TCP segments + UDP) sent through - * the network for this UID. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. + * Return number of packets transmitted by the given UID since device boot. + * Counts packets across all network interfaces, and always increases + * monotonically since device boot. Statistics are measured at the network + * layer, so they include both TCP and UDP usage. + * <p> + * Before {@link android.os.Build.VERSION_CODES#K}, this may return + * {@link #UNSUPPORTED} on devices where statistics aren't available. * - * @param uid The UID of the process to examine. - * @return number of packets. - * If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @see android.os.Process#myUid() + * @see android.content.pm.ApplicationInfo#uid */ - public static native long getUidTxPackets(int uid); + public static long getUidTxPackets(int uid) { + return nativeGetUidStat(uid, TYPE_TX_PACKETS); + } /** - * Get the number of packets (TCP segments + UDP) received through - * the network for this UID. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. + * Return number of packets received by the given UID since device boot. + * Counts packets across all network interfaces, and always increases + * monotonically since device boot. Statistics are measured at the network + * layer, so they include both TCP and UDP usage. + * <p> + * Before {@link android.os.Build.VERSION_CODES#K}, this may return + * {@link #UNSUPPORTED} on devices where statistics aren't available. * - * @param uid The UID of the process to examine. - * @return number of packets + * @see android.os.Process#myUid() + * @see android.content.pm.ApplicationInfo#uid */ - public static native long getUidRxPackets(int uid); + public static long getUidRxPackets(int uid) { + return nativeGetUidStat(uid, TYPE_RX_PACKETS); + } /** - * Get the number of TCP payload bytes sent for this UID. - * This total does not include protocol and control overheads at - * the transport and the lower layers of the networking stack. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of bytes. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidTxBytes(int) */ - public static native long getUidTcpTxBytes(int uid); + @Deprecated + public static long getUidTcpTxBytes(int uid) { + return UNSUPPORTED; + } /** - * Get the number of TCP payload bytes received for this UID. - * This total does not include protocol and control overheads at - * the transport and the lower layers of the networking stack. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of bytes. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidRxBytes(int) */ - public static native long getUidTcpRxBytes(int uid); + @Deprecated + public static long getUidTcpRxBytes(int uid) { + return UNSUPPORTED; + } /** - * Get the number of UDP payload bytes sent for this UID. - * This total does not include protocol and control overheads at - * the transport and the lower layers of the networking stack. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of bytes. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidTxBytes(int) */ - public static native long getUidUdpTxBytes(int uid); + @Deprecated + public static long getUidUdpTxBytes(int uid) { + return UNSUPPORTED; + } /** - * Get the number of UDP payload bytes received for this UID. - * This total does not include protocol and control overheads at - * the transport and the lower layers of the networking stack. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of bytes. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidRxBytes(int) */ - public static native long getUidUdpRxBytes(int uid); + @Deprecated + public static long getUidUdpRxBytes(int uid) { + return UNSUPPORTED; + } /** - * Get the number of TCP segments sent for this UID. - * Does not include TCP control packets (SYN/ACKs/FIN/..). - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of TCP segments. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidTxPackets(int) */ - public static native long getUidTcpTxSegments(int uid); + @Deprecated + public static long getUidTcpTxSegments(int uid) { + return UNSUPPORTED; + } /** - * Get the number of TCP segments received for this UID. - * Does not include TCP control packets (SYN/ACKs/FIN/..). - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of TCP segments. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidRxPackets(int) */ - public static native long getUidTcpRxSegments(int uid); + @Deprecated + public static long getUidTcpRxSegments(int uid) { + return UNSUPPORTED; + } /** - * Get the number of UDP packets sent for this UID. - * Includes DNS requests. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of packets. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidTxPackets(int) */ - public static native long getUidUdpTxPackets(int uid); + @Deprecated + public static long getUidUdpTxPackets(int uid) { + return UNSUPPORTED; + } /** - * Get the number of UDP packets received for this UID. - * Includes DNS responses. - * The statistics are across all interfaces. - * - * {@see android.os.Process#myUid()}. - * - * @param uid The UID of the process to examine. - * @return number of packets. If the statistics are not supported by this device, - * {@link #UNSUPPORTED} will be returned. + * @deprecated Starting in {@link android.os.Build.VERSION_CODES#K}, + * transport layer statistics are no longer available, and will + * always return {@link #UNSUPPORTED}. + * @see #getUidRxPackets(int) */ - public static native long getUidUdpRxPackets(int uid); + @Deprecated + public static long getUidUdpRxPackets(int uid) { + return UNSUPPORTED; + } /** * Return detailed {@link NetworkStats} for the current UID. Requires no @@ -587,7 +606,10 @@ public class TrafficStats { private static final int TYPE_RX_PACKETS = 1; private static final int TYPE_TX_BYTES = 2; private static final int TYPE_TX_PACKETS = 3; + private static final int TYPE_TCP_RX_PACKETS = 4; + private static final int TYPE_TCP_TX_PACKETS = 5; private static native long nativeGetTotalStat(int type); private static native long nativeGetIfaceStat(String iface, int type); + private static native long nativeGetUidStat(int uid, int type); } diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index fabe018..04f3974 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -17,6 +17,7 @@ package android.net.http; import com.android.internal.http.HttpDateTime; + import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; @@ -25,18 +26,18 @@ import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; -import org.apache.http.entity.AbstractHttpEntity; -import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.params.HttpClientParams; +import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; @@ -44,26 +45,26 @@ import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; +import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.BasicHttpContext; -import java.io.IOException; -import java.io.InputStream; -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import java.net.URI; - -import android.content.Context; import android.content.ContentResolver; +import android.content.Context; import android.net.SSLCertificateSocketFactory; import android.net.SSLSessionCache; import android.os.Looper; import android.util.Base64; import android.util.Log; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + /** * Implementation of the Apache {@link DefaultHttpClient} that is configured with * reasonable default settings and registered schemes for Android. @@ -266,7 +267,7 @@ public final class AndroidHttpClient implements HttpClient { return delegate.execute(target, request, context); } - public <T> T execute(HttpUriRequest request, + public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException { return delegate.execute(request, responseHandler); @@ -404,6 +405,11 @@ public final class AndroidHttpClient implements HttpClient { builder.append("curl "); + // add in the method + builder.append("-X "); + builder.append(request.getMethod()); + builder.append(" "); + for (Header header: request.getAllHeaders()) { if (!logAuthToken && (header.getName().equals("Authorization") || diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index 53b41d5..7c3123f 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -197,7 +197,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub isResumed = state.resumed; } if (isResumed) { - requestNfcServiceCallback(true); + requestNfcServiceCallback(); } } @@ -211,7 +211,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub isResumed = state.resumed; } if (isResumed) { - requestNfcServiceCallback(true); + requestNfcServiceCallback(); } } @@ -223,7 +223,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub isResumed = state.resumed; } if (isResumed) { - requestNfcServiceCallback(true); + requestNfcServiceCallback(); } } @@ -236,7 +236,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub isResumed = state.resumed; } if (isResumed) { - requestNfcServiceCallback(true); + requestNfcServiceCallback(); } } @@ -249,18 +249,17 @@ public final class NfcActivityManager extends INdefPushCallback.Stub isResumed = state.resumed; } if (isResumed) { - requestNfcServiceCallback(true); + requestNfcServiceCallback(); } } /** * Request or unrequest NFC service callbacks for NDEF push. * Makes IPC call - do not hold lock. - * TODO: Do not do IPC on every onPause/onResume */ - void requestNfcServiceCallback(boolean request) { + void requestNfcServiceCallback() { try { - NfcAdapter.sService.setNdefPushCallback(request ? this : null); + NfcAdapter.sService.setNdefPushCallback(this); } catch (RemoteException e) { mAdapter.attemptDeadServiceRecovery(e); } @@ -355,7 +354,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub if (state == null) return; state.resumed = true; } - requestNfcServiceCallback(true); + requestNfcServiceCallback(); } /** Callback from Activity life-cycle, on main thread */ @@ -367,7 +366,6 @@ public final class NfcActivityManager extends INdefPushCallback.Stub if (state == null) return; state.resumed = false; } - requestNfcServiceCallback(false); } /** Callback from Activity life-cycle, on main thread */ diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 4baceed..6ad382b 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -174,31 +174,25 @@ public final class NfcAdapter { * Broadcast Action: The state of the local NFC adapter has been * changed. * <p>For example, NFC has been turned on or off. - * <p>Always contains the extra field {@link #EXTRA_STATE} - * @hide + * <p>Always contains the extra field {@link #EXTRA_ADAPTER_STATE} */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED"; /** - * Used as an int extra field in {@link #ACTION_STATE_CHANGED} + * Used as an int extra field in {@link #ACTION_ADAPTER_STATE_CHANGED} * intents to request the current power state. Possible values are: * {@link #STATE_OFF}, * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, - * @hide */ public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; - /** @hide */ public static final int STATE_OFF = 1; - /** @hide */ public static final int STATE_TURNING_ON = 2; - /** @hide */ public static final int STATE_ON = 3; - /** @hide */ public static final int STATE_TURNING_OFF = 4; /** @hide */ diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 9821824..499ec77 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -93,6 +93,11 @@ public abstract class BatteryStats implements Parcelable { public static final int VIDEO_TURNED_ON = 8; /** + * A constant indicating a vibrator on timer + */ + public static final int VIBRATOR_ON = 9; + + /** * Include all of the data in the stats, including previously saved data. */ public static final int STATS_SINCE_CHARGED = 0; @@ -131,6 +136,7 @@ public abstract class BatteryStats implements Parcelable { private static final String APK_DATA = "apk"; private static final String PROCESS_DATA = "pr"; private static final String SENSOR_DATA = "sr"; + private static final String VIBRATOR_DATA = "vib"; private static final String WAKELOCK_DATA = "wl"; private static final String KERNEL_WAKELOCK_DATA = "kwl"; private static final String NETWORK_DATA = "nt"; @@ -277,6 +283,7 @@ public abstract class BatteryStats implements Parcelable { int which); public abstract long getAudioTurnedOnTime(long batteryRealtime, int which); public abstract long getVideoTurnedOnTime(long batteryRealtime, int which); + public abstract Timer getVibratorOnTimer(); /** * Note that these must match the constants in android.os.PowerManager. @@ -294,6 +301,11 @@ public abstract class BatteryStats implements Parcelable { public abstract int getUserActivityCount(int type, int which); public static abstract class Sensor { + /* + * FIXME: it's not correct to use this magic value because it + * could clash with a sensor handle (which are defined by + * the sensor HAL, and therefore out of our control + */ // Magic sensor number for the GPS. public static final int GPS = -10000; @@ -1395,6 +1407,16 @@ public abstract class BatteryStats implements Parcelable { } } + Timer vibTimer = u.getVibratorOnTimer(); + if (vibTimer != null) { + // Convert from microseconds to milliseconds with rounding + long totalTime = (vibTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000; + int count = vibTimer.getCountLocked(which); + if (totalTime != 0) { + dumpLine(pw, uid, category, VIBRATOR_DATA, totalTime, count); + } + } + Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); if (processStats.size() > 0) { for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent @@ -1919,6 +1941,26 @@ public abstract class BatteryStats implements Parcelable { } } + Timer vibTimer = u.getVibratorOnTimer(); + if (vibTimer != null) { + // Convert from microseconds to milliseconds with rounding + long totalTime = (vibTimer.getTotalTimeLocked( + batteryRealtime, which) + 500) / 1000; + int count = vibTimer.getCountLocked(which); + //timer.logState(); + if (totalTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Vibrator: "); + formatTimeMs(sb, totalTime); + sb.append("realtime ("); + sb.append(count); + sb.append(" times)"); + pw.println(sb.toString()); + uidActivity = true; + } + } + Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); if (processStats.size() > 0) { for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a7f39d5..52c89e8 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -431,6 +431,11 @@ public class Build { * </ul> */ public static final int JELLY_BEAN_MR1 = 17; + + /** + * Android X.X: "K". Just "K" + */ + public static final int K = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 460a5fe..65eefcb 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -1061,10 +1061,7 @@ public final class Bundle implements Parcelable, Cloneable { */ public String getString(String key) { unparcel(); - Object o = mMap.get(key); - if (o == null) { - return null; - } + final Object o = mMap.get(key); try { return (String) o; } catch (ClassCastException e) { @@ -1079,20 +1076,12 @@ public final class Bundle implements Parcelable, Cloneable { * * @param key a String, or null * @param defaultValue Value to return if key does not exist - * @return a String value, or null + * @return the String value associated with the given key, or defaultValue + * if no valid String object is currently mapped to that key. */ public String getString(String key, String defaultValue) { - unparcel(); - Object o = mMap.get(key); - if (o == null) { - return defaultValue; - } - try { - return (String) o; - } catch (ClassCastException e) { - typeWarning(key, o, "String", e); - return defaultValue; - } + final String s = getString(key); + return (s == null) ? defaultValue : s; } /** @@ -1105,10 +1094,7 @@ public final class Bundle implements Parcelable, Cloneable { */ public CharSequence getCharSequence(String key) { unparcel(); - Object o = mMap.get(key); - if (o == null) { - return null; - } + final Object o = mMap.get(key); try { return (CharSequence) o; } catch (ClassCastException e) { @@ -1123,20 +1109,12 @@ public final class Bundle implements Parcelable, Cloneable { * * @param key a String, or null * @param defaultValue Value to return if key does not exist - * @return a CharSequence value, or null + * @return the CharSequence value associated with the given key, or defaultValue + * if no valid CharSequence object is currently mapped to that key. */ public CharSequence getCharSequence(String key, CharSequence defaultValue) { - unparcel(); - Object o = mMap.get(key); - if (o == null) { - return defaultValue; - } - try { - return (CharSequence) o; - } catch (ClassCastException e) { - typeWarning(key, o, "CharSequence", e); - return defaultValue; - } + final CharSequence cs = getCharSequence(key); + return (cs == null) ? defaultValue : cs; } /** diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 2179fa1..c765457 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -306,23 +306,6 @@ interface INetworkManagementService boolean isBandwidthControlEnabled(); /** - * Configures bandwidth throttling on an interface. - */ - void setInterfaceThrottle(String iface, int rxKbps, int txKbps); - - /** - * Returns the currently configured RX throttle values - * for the specified interface - */ - int getInterfaceRxThrottle(String iface); - - /** - * Returns the currently configured TX throttle values - * for the specified interface - */ - int getInterfaceTxThrottle(String iface); - - /** * Sets idletimer for an interface. * * This either initializes a new idletimer or increases its @@ -351,7 +334,7 @@ interface INetworkManagementService /** * Bind name servers to an interface in the DNS resolver. */ - void setDnsServersForInterface(String iface, in String[] servers); + void setDnsServersForInterface(String iface, in String[] servers, String domains); /** * Flush the DNS cache associated with the default interface. @@ -369,4 +352,14 @@ interface INetworkManagementService void setFirewallEgressSourceRule(String addr, boolean allow); void setFirewallEgressDestRule(String addr, int port, boolean allow); void setFirewallUidRule(int uid, boolean allow); + + /** + * Set a process (pid) to use the name servers associated with the specified interface. + */ + void setDnsInterfaceForPid(String iface, int pid); + + /** + * Clear a process (pid) from being associated with an interface. + */ + void clearDnsInterfaceForPid(int pid); } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index ec02ae0..34c9740 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -17,6 +17,7 @@ package android.os; +import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -37,4 +38,6 @@ interface IUserManager { void wipeUser(int userHandle); int getUserSerialNumber(int userHandle); int getUserHandle(int userSerialNumber); + Bundle getUserRestrictions(int userHandle); + void setUserRestrictions(in Bundle restrictions, int userHandle); } diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 2c2fe8a..456ffb1 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -20,8 +20,8 @@ package android.os; interface IVibratorService { boolean hasVibrator(); - void vibrate(long milliseconds, IBinder token); - void vibratePattern(in long[] pattern, int repeat, IBinder token); + void vibrate(int uid, String packageName, long milliseconds, IBinder token); + void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, IBinder token); void cancelVibrate(IBinder token); } diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index 5ad60ec..222578a 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -48,10 +48,10 @@ public class MessageQueue { // Barriers are indicated by messages with a null target whose arg1 field carries the token. private int mNextBarrierToken; - private native void nativeInit(); - private native void nativeDestroy(); - private native void nativePollOnce(int ptr, int timeoutMillis); - private native void nativeWake(int ptr); + private native static int nativeInit(); + private native static void nativeDestroy(int ptr); + private native static void nativePollOnce(int ptr, int timeoutMillis); + private native static void nativeWake(int ptr); /** * Callback interface for discovering when a thread is going to block @@ -102,18 +102,25 @@ public class MessageQueue { MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; - nativeInit(); + mPtr = nativeInit(); } @Override protected void finalize() throws Throwable { try { - nativeDestroy(); + dispose(); } finally { super.finalize(); } } + private void dispose() { + if (mPtr != 0) { + nativeDestroy(mPtr); + mPtr = 0; + } + } + final Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; @@ -126,6 +133,7 @@ public class MessageQueue { synchronized (this) { if (mQuiting) { + dispose(); return null; } diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java index 8de4e06..ac6027f 100644 --- a/core/java/android/os/NullVibrator.java +++ b/core/java/android/os/NullVibrator.java @@ -49,6 +49,22 @@ public class NullVibrator extends Vibrator { } } + /** + * @hide + */ + @Override + public void vibrate(int owningUid, String owningPackage, long milliseconds) { + vibrate(milliseconds); + } + + /** + * @hide + */ + @Override + public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) { + vibrate(pattern, repeat); + } + @Override public void cancel() { } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 788ab74..31d323b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -1254,6 +1254,12 @@ public final class Parcel { p.writeToParcel(this, parcelableFlags); } + /** @hide */ + public final void writeParcelableCreator(Parcelable p) { + String name = p.getClass().getName(); + writeString(name); + } + /** * Write a generic serializable object in to a Parcel. It is strongly * recommended that this method be avoided, since the serialization @@ -2046,6 +2052,28 @@ public final class Parcel { * was an error trying to instantiate the Parcelable. */ public final <T extends Parcelable> T readParcelable(ClassLoader loader) { + Parcelable.Creator<T> creator = readParcelableCreator(loader); + if (creator == null) { + return null; + } + if (creator instanceof Parcelable.ClassLoaderCreator<?>) { + return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader); + } + return creator.createFromParcel(this); + } + + /** @hide */ + public final <T extends Parcelable> T readCreator(Parcelable.Creator<T> creator, + ClassLoader loader) { + if (creator instanceof Parcelable.ClassLoaderCreator<?>) { + return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader); + } + return creator.createFromParcel(this); + } + + /** @hide */ + public final <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator( + ClassLoader loader) { String name = readString(); if (name == null) { return null; @@ -2087,6 +2115,10 @@ public final class Parcel { + "Parcelable.Creator object called " + " CREATOR on class " + name); } + catch (NullPointerException e) { + throw new BadParcelableException("Parcelable protocol requires " + + "the CREATOR object to be static on class " + name); + } if (creator == null) { throw new BadParcelableException("Parcelable protocol requires a " + "Parcelable.Creator object called " @@ -2097,10 +2129,7 @@ public final class Parcel { } } - if (creator instanceof Parcelable.ClassLoaderCreator<?>) { - return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader); - } - return creator.createFromParcel(this); + return creator; } /** diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index ec660ee..3de362c 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -108,13 +108,6 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException { String path = file.getPath(); - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(path); - if ((mode&MODE_WRITE_ONLY) != 0) { - security.checkWrite(path); - } - } if ((mode&MODE_READ_WRITE) == 0) { throw new IllegalArgumentException( diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 05099fb..facab4c 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -806,7 +806,15 @@ public class Process { */ public static final native void setProcessGroup(int pid, int group) throws IllegalArgumentException, SecurityException; - + + /** + * Return the scheduling group of requested process. + * + * @hide + */ + public static final native int getProcessGroup(int pid) + throws IllegalArgumentException, SecurityException; + /** * Set the priority of the calling thread, based on Linux priorities. See * {@link #setThreadPriority(int, int)} for more information. diff --git a/core/java/android/os/SchedulingPolicyService.java b/core/java/android/os/SchedulingPolicyService.java deleted file mode 100644 index a3fede6..0000000 --- a/core/java/android/os/SchedulingPolicyService.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.Process; -import android.util.Log; - -/** - * The implementation of the scheduling policy service interface. - * - * @hide - */ -public class SchedulingPolicyService extends ISchedulingPolicyService.Stub { - - private static final String TAG = "SchedulingPolicyService"; - - // Minimum and maximum values allowed for requestPriority parameter prio - private static final int PRIORITY_MIN = 1; - private static final int PRIORITY_MAX = 3; - - public SchedulingPolicyService() { - } - - public int requestPriority(int pid, int tid, int prio) { - //Log.i(TAG, "requestPriority(pid=" + pid + ", tid=" + tid + ", prio=" + prio + ")"); - - // Verify that caller is mediaserver, priority is in range, and that the - // callback thread specified by app belongs to the app that called mediaserver. - // Once we've verified that the caller is mediaserver, we can trust the pid but - // we can't trust the tid. No need to explicitly check for pid == 0 || tid == 0, - // since if not the case then the getThreadGroupLeader() test will also fail. - if (Binder.getCallingUid() != Process.MEDIA_UID || prio < PRIORITY_MIN || - prio > PRIORITY_MAX || Process.getThreadGroupLeader(tid) != pid) { - return PackageManager.PERMISSION_DENIED; - } - try { - // make good use of our CAP_SYS_NICE capability - Process.setThreadGroup(tid, Binder.getCallingPid() == pid ? - Process.THREAD_GROUP_AUDIO_SYS : Process.THREAD_GROUP_AUDIO_APP); - // must be in this order or it fails the schedulability constraint - Process.setThreadScheduler(tid, Process.SCHED_FIFO, prio); - } catch (RuntimeException e) { - return PackageManager.PERMISSION_DENIED; - } - return PackageManager.PERMISSION_GRANTED; - } - -} diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java index ca7fdba..60ec0d7 100644 --- a/core/java/android/os/StatFs.java +++ b/core/java/android/os/StatFs.java @@ -65,6 +65,14 @@ public class StatFs { } /** + * The size, in bytes, of a block on the file system. This corresponds to + * the Unix {@code statfs.f_bsize} field. + */ + public long getBlockSizeLong() { + return mStat.f_bsize; + } + + /** * The total number of blocks on the file system. This corresponds to the * Unix {@code statfs.f_blocks} field. */ @@ -73,6 +81,14 @@ public class StatFs { } /** + * The size, in bytes, of a block on the file system. This corresponds to + * the Unix {@code statfs.f_bsize} field. + */ + public long getBlockCountLong() { + return mStat.f_blocks; + } + + /** * The total number of blocks that are free on the file system, including * reserved blocks (that are not available to normal applications). This * corresponds to the Unix {@code statfs.f_bfree} field. Most applications @@ -83,10 +99,44 @@ public class StatFs { } /** + * The total number of blocks that are free on the file system, including + * reserved blocks (that are not available to normal applications). This + * corresponds to the Unix {@code statfs.f_bfree} field. Most applications + * will want to use {@link #getAvailableBlocks()} instead. + */ + public long getFreeBlocksLong() { + return mStat.f_bfree; + } + + /** + * The number of bytes that are free on the file system, including + * reserved blocks (that are not available to normal applications). + */ + public long getFreeBytes() { + return mStat.f_bfree * mStat.f_bsize; + } + + /** * The number of blocks that are free on the file system and available to * applications. This corresponds to the Unix {@code statfs.f_bavail} field. */ public int getAvailableBlocks() { return (int) mStat.f_bavail; } + + /** + * The number of blocks that are free on the file system and available to + * applications. This corresponds to the Unix {@code statfs.f_bavail} field. + */ + public long getAvailableBlocksLong() { + return mStat.f_bavail; + } + + /** + * The number of bytes that are free on the file system and available to + * applications. + */ + public long getAvailableBytes() { + return mStat.f_bavail * mStat.f_bsize; + } } diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index 7c5a47e..e66fb28 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -16,6 +16,8 @@ package android.os; +import android.app.ActivityThread; +import android.content.Context; import android.util.Log; /** @@ -26,10 +28,18 @@ import android.util.Log; public class SystemVibrator extends Vibrator { private static final String TAG = "Vibrator"; + private final String mPackageName; private final IVibratorService mService; private final Binder mToken = new Binder(); public SystemVibrator() { + mPackageName = ActivityThread.currentPackageName(); + mService = IVibratorService.Stub.asInterface( + ServiceManager.getService("vibrator")); + } + + public SystemVibrator(Context context) { + mPackageName = context.getBasePackageName(); mService = IVibratorService.Stub.asInterface( ServiceManager.getService("vibrator")); } @@ -49,19 +59,35 @@ public class SystemVibrator extends Vibrator { @Override public void vibrate(long milliseconds) { + vibrate(Process.myUid(), mPackageName, milliseconds); + } + + @Override + public void vibrate(long[] pattern, int repeat) { + vibrate(Process.myUid(), mPackageName, pattern, repeat); + } + + /** + * @hide + */ + @Override + public void vibrate(int owningUid, String owningPackage, long milliseconds) { if (mService == null) { Log.w(TAG, "Failed to vibrate; no vibrator service."); return; } try { - mService.vibrate(milliseconds, mToken); + mService.vibrate(owningUid, owningPackage, milliseconds, mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); } } + /** + * @hide + */ @Override - public void vibrate(long[] pattern, int repeat) { + public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) { if (mService == null) { Log.w(TAG, "Failed to vibrate; no vibrator service."); return; @@ -71,7 +97,7 @@ public class SystemVibrator extends Vibrator { // anyway if (repeat < pattern.length) { try { - mService.vibratePattern(pattern, repeat, mToken); + mService.vibratePattern(owningUid, owningPackage, pattern, repeat, mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 0ca9183..27ed6b6 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -31,7 +31,7 @@ import android.util.Log; public final class Trace { private static final String TAG = "Trace"; - // These tags must be kept in sync with frameworks/native/include/utils/Trace.h. + // These tags must be kept in sync with system/core/include/cutils/trace.h. public static final long TRACE_TAG_NEVER = 0; public static final long TRACE_TAG_ALWAYS = 1L << 0; public static final long TRACE_TAG_GRAPHICS = 1L << 1; @@ -44,12 +44,13 @@ public final class Trace { public static final long TRACE_TAG_AUDIO = 1L << 8; public static final long TRACE_TAG_VIDEO = 1L << 9; public static final long TRACE_TAG_CAMERA = 1L << 10; + public static final long TRACE_TAG_HAL = 1L << 11; private static final long TRACE_TAG_NOT_READY = 1L << 63; public static final int TRACE_FLAGS_START_BIT = 1; public static final String[] TRACE_TAGS = { "Graphics", "Input", "View", "WebView", "Window Manager", - "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", + "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", "HAL", }; public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags"; diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index cc96152..d205253 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -16,6 +16,8 @@ package android.os; +import java.io.PrintWriter; + /** * Representation of a user on the device. */ @@ -152,6 +154,50 @@ public final class UserHandle implements Parcelable { } /** + * Generate a text representation of the uid, breaking out its individual + * components -- user, app, isolated, etc. + * @hide + */ + public static void formatUid(StringBuilder sb, int uid) { + if (uid < Process.FIRST_APPLICATION_UID) { + sb.append(uid); + } else { + sb.append('u'); + sb.append(getUserId(uid)); + final int appId = getAppId(uid); + if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { + sb.append('i'); + sb.append(appId - Process.FIRST_ISOLATED_UID); + } else { + sb.append('a'); + sb.append(appId); + } + } + } + + /** + * Generate a text representation of the uid, breaking out its individual + * components -- user, app, isolated, etc. + * @hide + */ + public static void formatUid(PrintWriter pw, int uid) { + if (uid < Process.FIRST_APPLICATION_UID) { + pw.print(uid); + } else { + pw.print('u'); + pw.print(getUserId(uid)); + final int appId = getAppId(uid); + if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { + pw.print('i'); + pw.print(appId - Process.FIRST_ISOLATED_UID); + } else { + pw.print('a'); + pw.print(appId); + } + } + } + + /** * Returns the user id of the current process * @return user id of the current process * @hide diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index d73f99a..e4a5e7f 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -35,6 +35,42 @@ public class UserManager { private final IUserManager mService; private final Context mContext; + /** + * @hide + * Key for user restrictions. Specifies if a user is allowed to add or remove accounts. + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String ALLOW_MODIFY_ACCOUNTS = "modify_accounts"; + + /** + * @hide + * Key for user restrictions. Specifies if a user is allowed to change Wi-Fi access points. + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String ALLOW_CONFIG_WIFI = "config_wifi"; + + /** + * @hide + * Key for user restrictions. Specifies if a user is allowed to install applications. + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String ALLOW_INSTALL_APPS = "install_apps"; + + /** + * @hide + * Key for user restrictions. Specifies if a user is allowed to uninstall applications. + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String ALLOW_UNINSTALL_APPS = "uninstall_apps"; + /** @hide */ public UserManager(Context context, IUserManager service) { mService = service; @@ -132,6 +168,35 @@ public class UserManager { } } + /** @hide */ + public Bundle getUserRestrictions() { + return getUserRestrictions(Process.myUserHandle()); + } + + /** @hide */ + public Bundle getUserRestrictions(UserHandle userHandle) { + try { + return mService.getUserRestrictions(userHandle.getIdentifier()); + } catch (RemoteException re) { + Log.w(TAG, "Could not get user restrictions", re); + return Bundle.EMPTY; + } + } + + /** @hide */ + public void setUserRestrictions(Bundle restrictions) { + setUserRestrictions(restrictions, Process.myUserHandle()); + } + + /** @hide */ + public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) { + try { + mService.setUserRestrictions(restrictions, userHandle.getIdentifier()); + } catch (RemoteException re) { + Log.w(TAG, "Could not set user restrictions", re); + } + } + /** * Return the serial number for a user. This is a device-unique * number assigned to that user; if the user is deleted and then a new diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index b67be4b..6650fca 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -73,6 +73,20 @@ public abstract class Vibrator { public abstract void vibrate(long[] pattern, int repeat); /** + * @hide + * Like {@link #vibrate(long)}, but allowing the caller to specify that + * the vibration is owned by someone else. + */ + public abstract void vibrate(int owningUid, String owningPackage, long milliseconds); + + /** + * @hide + * Like {@link #vibrate(long[], int)}, but allowing the caller to specify that + * the vibration is owned by someone else. + */ + public abstract void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat); + + /** * Turn the vibrator off. * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#VIBRATE}. diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index ba77df7..b79bdee 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -1,6 +1,6 @@ package android.os; -import com.android.internal.util.ArrayUtils; +import android.util.Log; import java.util.Arrays; @@ -10,8 +10,12 @@ import java.util.Arrays; * defined; this is an opaque container. */ public class WorkSource implements Parcelable { + static final String TAG = "WorkSource"; + static final boolean DEBUG = false; + int mNum; int[] mUids; + String[] mNames; /** * Internal statics to avoid object allocations in some operations. @@ -47,8 +51,10 @@ public class WorkSource implements Parcelable { mNum = orig.mNum; if (orig.mUids != null) { mUids = orig.mUids.clone(); + mNames = orig.mNames != null ? orig.mNames.clone() : null; } else { mUids = null; + mNames = null; } } @@ -56,11 +62,23 @@ public class WorkSource implements Parcelable { public WorkSource(int uid) { mNum = 1; mUids = new int[] { uid, 0 }; + mNames = null; + } + + /** @hide */ + public WorkSource(int uid, String name) { + if (name == null) { + throw new NullPointerException("Name can't be null"); + } + mNum = 1; + mUids = new int[] { uid, 0 }; + mNames = new String[] { name, null }; } WorkSource(Parcel in) { mNum = in.readInt(); mUids = in.createIntArray(); + mNames = in.createStringArray(); } /** @hide */ @@ -73,6 +91,11 @@ public class WorkSource implements Parcelable { return mUids[index]; } + /** @hide */ + public String getName(int index) { + return mNames != null ? mNames[index] : null; + } + /** * Clear this WorkSource to be empty. */ @@ -91,6 +114,11 @@ public class WorkSource implements Parcelable { for (int i = 0; i < mNum; i++) { result = ((result << 4) | (result >>> 28)) ^ mUids[i]; } + if (mNames != null) { + for (int i = 0; i < mNum; i++) { + result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode(); + } + } return result; } @@ -106,10 +134,15 @@ public class WorkSource implements Parcelable { } final int[] uids1 = mUids; final int[] uids2 = other.mUids; + final String[] names1 = mNames; + final String[] names2 = other.mNames; for (int i=0; i<N; i++) { if (uids1[i] != uids2[i]) { return true; } + if (names1 != null && names2 != null && !names1[i].equals(names2[i])) { + return true; + } } return false; } @@ -131,8 +164,18 @@ public class WorkSource implements Parcelable { } else { mUids = other.mUids.clone(); } + if (other.mNames != null) { + if (mNames != null && mNames.length >= mNum) { + System.arraycopy(other.mNames, 0, mNames, 0, mNum); + } else { + mNames = other.mNames.clone(); + } + } else { + mNames = null; + } } else { mUids = null; + mNames = null; } } @@ -141,6 +184,22 @@ public class WorkSource implements Parcelable { mNum = 1; if (mUids == null) mUids = new int[2]; mUids[0] = uid; + mNames = null; + } + + /** @hide */ + public void set(int uid, String name) { + if (name == null) { + throw new NullPointerException("Name can't be null"); + } + mNum = 1; + if (mUids == null) { + mUids = new int[2]; + mNames = new String[2]; + } + mUids[0] = uid; + mNames[0] = name; + mNames = null; } /** @hide */ @@ -182,10 +241,49 @@ public class WorkSource implements Parcelable { /** @hide */ public boolean add(int uid) { - synchronized (sTmpWorkSource) { - sTmpWorkSource.mUids[0] = uid; - return updateLocked(sTmpWorkSource, false, false); + if (mNum <= 0) { + mNames = null; + insert(0, uid); + return true; + } + if (mNames != null) { + throw new IllegalArgumentException("Adding without name to named " + this); + } + int i = Arrays.binarySearch(mUids, 0, mNum, uid); + if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i); + if (i >= 0) { + return false; } + insert(-i-1, uid); + return true; + } + + /** @hide */ + public boolean add(int uid, String name) { + if (mNum <= 0) { + insert(0, uid, name); + return true; + } + if (mNames == null) { + throw new IllegalArgumentException("Adding name to unnamed " + this); + } + int i; + for (i=0; i<mNum; i++) { + if (mUids[i] > uid) { + break; + } + if (mUids[i] == uid) { + int diff = mNames[i].compareTo(name); + if (diff > 0) { + break; + } + if (diff == 0) { + return false; + } + } + } + insert(i, uid, name); + return true; } /** @hide */ @@ -199,19 +297,102 @@ public class WorkSource implements Parcelable { } public boolean remove(WorkSource other) { + if (mNum <= 0 || other.mNum <= 0) { + return false; + } + if (mNames == null && other.mNames == null) { + return removeUids(other); + } else { + if (mNames == null) { + throw new IllegalArgumentException("Other " + other + " has names, but target " + + this + " does not"); + } + if (other.mNames == null) { + throw new IllegalArgumentException("Target " + this + " has names, but other " + + other + " does not"); + } + return removeUidsAndNames(other); + } + } + + /** @hide */ + public WorkSource stripNames() { + if (mNum <= 0) { + return new WorkSource(); + } + WorkSource result = new WorkSource(); + int lastUid = -1; + for (int i=0; i<mNum; i++) { + int uid = mUids[i]; + if (i == 0 || lastUid != uid) { + result.add(uid); + } + } + return result; + } + + private boolean removeUids(WorkSource other) { int N1 = mNum; final int[] uids1 = mUids; final int N2 = other.mNum; final int[] uids2 = other.mUids; boolean changed = false; - int i1 = 0; - for (int i2=0; i2<N2 && i1<N1; i2++) { + int i1 = 0, i2 = 0; + if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this); + while (i1 < N1 && i2 < N2) { + if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2 + + " of " + N2); if (uids2[i2] == uids1[i1]) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + + ": remove " + uids1[i1]); N1--; + changed = true; if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1); + i2++; + } else if (uids2[i2] > uids1[i1]) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1"); + i1++; + } else { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2"); + i2++; } - while (i1 < N1 && uids2[i2] > uids1[i1]) { + } + + mNum = N1; + + return changed; + } + + private boolean removeUidsAndNames(WorkSource other) { + int N1 = mNum; + final int[] uids1 = mUids; + final String[] names1 = mNames; + final int N2 = other.mNum; + final int[] uids2 = other.mUids; + final String[] names2 = other.mNames; + boolean changed = false; + int i1 = 0, i2 = 0; + if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this); + while (i1 < N1 && i2 < N2) { + if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2 + + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]); + if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + + ": remove " + uids1[i1] + " " + names1[i1]); + N1--; + changed = true; + if (i1 < N1) { + System.arraycopy(uids1, i1+1, uids1, i1, N1-i1); + System.arraycopy(names1, i1+1, names1, i1, N1-i1); + } + i2++; + } else if (uids2[i2] > uids1[i1] + || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1"); i1++; + } else { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2"); + i2++; } } @@ -221,20 +402,50 @@ public class WorkSource implements Parcelable { } private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) { + if (mNames == null && other.mNames == null) { + return updateUidsLocked(other, set, returnNewbs); + } else { + if (mNum > 0 && mNames == null) { + throw new IllegalArgumentException("Other " + other + " has names, but target " + + this + " does not"); + } + if (other.mNum > 0 && other.mNames == null) { + throw new IllegalArgumentException("Target " + this + " has names, but other " + + other + " does not"); + } + return updateUidsAndNamesLocked(other, set, returnNewbs); + } + } + + private static WorkSource addWork(WorkSource cur, int newUid) { + if (cur == null) { + return new WorkSource(newUid); + } + cur.insert(cur.mNum, newUid); + return cur; + } + + private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) { int N1 = mNum; int[] uids1 = mUids; final int N2 = other.mNum; final int[] uids2 = other.mUids; boolean changed = false; - int i1 = 0; - for (int i2=0; i2<N2; i2++) { - if (i1 >= N1 || uids2[i2] < uids1[i1]) { + int i1 = 0, i2 = 0; + if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set + + " returnNewbs=" + returnNewbs); + while (i1 < N1 || i2 < N2) { + if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2 + + " of " + N2); + if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) { // Need to insert a new uid. + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + + ": insert " + uids2[i2]); changed = true; if (uids1 == null) { uids1 = new int[4]; uids1[0] = uids2[i2]; - } else if (i1 >= uids1.length) { + } else if (N1 >= uids1.length) { int[] newuids = new int[(uids1.length*3)/2]; if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1); if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1); @@ -245,39 +456,37 @@ public class WorkSource implements Parcelable { uids1[i1] = uids2[i2]; } if (returnNewbs) { - if (sNewbWork == null) { - sNewbWork = new WorkSource(uids2[i2]); - } else { - sNewbWork.addLocked(uids2[i2]); - } + sNewbWork = addWork(sNewbWork, uids2[i2]); } N1++; i1++; + i2++; } else { if (!set) { // Skip uids that already exist or are not in 'other'. - do { - i1++; - } while (i1 < N1 && uids2[i2] >= uids1[i1]); + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip"); + if (i2 < N2 && uids2[i2] == uids1[i1]) { + i2++; + } + i1++; } else { // Remove any uids that don't exist in 'other'. int start = i1; - while (i1 < N1 && uids2[i2] > uids1[i1]) { - if (sGoneWork == null) { - sGoneWork = new WorkSource(uids1[i1]); - } else { - sGoneWork.addLocked(uids1[i1]); - } + while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]); + sGoneWork = addWork(sGoneWork, uids1[i1]); i1++; } if (start < i1) { - System.arraycopy(uids1, i1, uids1, start, i1-start); + System.arraycopy(uids1, i1, uids1, start, N1-i1); N1 -= i1-start; i1 = start; } // If there is a matching uid, skip it. - if (i1 < N1 && uids2[i1] == uids1[i1]) { + if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip"); i1++; + i2++; } } } @@ -289,21 +498,145 @@ public class WorkSource implements Parcelable { return changed; } - private void addLocked(int uid) { + /** + * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'. + */ + private int compare(WorkSource other, int i1, int i2) { + final int diff = mUids[i1] - other.mUids[i2]; + if (diff != 0) { + return diff; + } + return mNames[i1].compareTo(other.mNames[i2]); + } + + private static WorkSource addWork(WorkSource cur, int newUid, String newName) { + if (cur == null) { + return new WorkSource(newUid, newName); + } + cur.insert(cur.mNum, newUid, newName); + return cur; + } + + private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) { + final int N2 = other.mNum; + final int[] uids2 = other.mUids; + String[] names2 = other.mNames; + boolean changed = false; + int i1 = 0, i2 = 0; + if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set + + " returnNewbs=" + returnNewbs); + while (i1 < mNum || i2 < N2) { + if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2 + + " of " + N2); + int diff = -1; + if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) { + // Need to insert a new uid. + changed = true; + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + + ": insert " + uids2[i2] + " " + names2[i2]); + insert(i1, uids2[i2], names2[i2]); + if (returnNewbs) { + sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]); + } + i1++; + i2++; + } else { + if (!set) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip"); + if (i2 < N2 && diff == 0) { + i2++; + } + i1++; + } else { + // Remove any uids that don't exist in 'other'. + int start = i1; + while (diff < 0) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1] + + " " + mNames[i1]); + sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]); + i1++; + if (i1 >= mNum) { + break; + } + diff = i2 < N2 ? compare(other, i1, i2) : -1; + } + if (start < i1) { + System.arraycopy(mUids, i1, mUids, start, mNum-i1); + System.arraycopy(mNames, i1, mNames, start, mNum-i1); + mNum -= i1-start; + i1 = start; + } + // If there is a matching uid, skip it. + if (i1 < mNum && diff == 0) { + if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip"); + i1++; + i2++; + } + } + } + } + + return changed; + } + + private void insert(int index, int uid) { + if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid); if (mUids == null) { mUids = new int[4]; mUids[0] = uid; mNum = 1; - return; - } - if (mNum >= mUids.length) { + } else if (mNum >= mUids.length) { int[] newuids = new int[(mNum*3)/2]; - System.arraycopy(mUids, 0, newuids, 0, mNum); + if (index > 0) { + System.arraycopy(mUids, 0, newuids, 0, index); + } + if (index < mNum) { + System.arraycopy(mUids, index, newuids, index+1, mNum-index); + } mUids = newuids; + mUids[index] = uid; + mNum++; + } else { + if (index < mNum) { + System.arraycopy(mUids, index, mUids, index+1, mNum-index); + } + mUids[index] = uid; + mNum++; } + } - mUids[mNum] = uid; - mNum++; + private void insert(int index, int uid, String name) { + if (mUids == null) { + mUids = new int[4]; + mUids[0] = uid; + mNames = new String[4]; + mNames[0] = name; + mNum = 1; + } else if (mNum >= mUids.length) { + int[] newuids = new int[(mNum*3)/2]; + String[] newnames = new String[(mNum*3)/2]; + if (index > 0) { + System.arraycopy(mUids, 0, newuids, 0, index); + System.arraycopy(mNames, 0, newnames, 0, index); + } + if (index < mNum) { + System.arraycopy(mUids, index, newuids, index+1, mNum-index); + System.arraycopy(mNames, index, newnames, index+1, mNum-index); + } + mUids = newuids; + mNames = newnames; + mUids[index] = uid; + mNames[index] = name; + mNum++; + } else { + if (index < mNum) { + System.arraycopy(mUids, index, mUids, index+1, mNum-index); + System.arraycopy(mNames, index, mNames, index+1, mNum-index); + } + mUids[index] = uid; + mNames[index] = name; + mNum++; + } } @Override @@ -315,19 +648,24 @@ public class WorkSource implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mNum); dest.writeIntArray(mUids); + dest.writeStringArray(mNames); } @Override public String toString() { StringBuilder result = new StringBuilder(); - result.append("{WorkSource: uids=["); + result.append("WorkSource{"); for (int i = 0; i < mNum; i++) { if (i != 0) { result.append(", "); } result.append(mUids[i]); + if (mNames != null) { + result.append(" "); + result.append(mNames[i]); + } } - result.append("]}"); + result.append("}"); return result.toString(); } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 862a95c..f5e728d 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,7 +16,9 @@ package android.os.storage; -import android.app.NotificationManager; +import static android.net.TrafficStats.MB_IN_BYTES; + +import android.content.ContentResolver; import android.content.Context; import android.os.Environment; import android.os.Handler; @@ -25,6 +27,7 @@ import android.os.Message; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.provider.Settings; import android.util.Log; import android.util.SparseArray; @@ -54,20 +57,20 @@ import java.util.concurrent.atomic.AtomicInteger; * {@link android.content.Context#getSystemService(java.lang.String)} with an * argument of {@link android.content.Context#STORAGE_SERVICE}. */ - -public class StorageManager -{ +public class StorageManager { private static final String TAG = "StorageManager"; + private final ContentResolver mResolver; + /* * Our internal MountService binder reference */ - final private IMountService mMountService; + private final IMountService mMountService; /* * The looper target for callbacks */ - Looper mTgtLooper; + private final Looper mTgtLooper; /* * Target listener for binder callbacks @@ -308,16 +311,16 @@ public class StorageManager * * @hide */ - public StorageManager(Looper tgtLooper) throws RemoteException { + public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException { + mResolver = resolver; + mTgtLooper = tgtLooper; mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); if (mMountService == null) { Log.e(TAG, "Unable to connect to mount service! - is it running yet?"); return; } - mTgtLooper = tgtLooper; } - /** * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. * @@ -610,4 +613,36 @@ public class StorageManager Log.w(TAG, "No primary storage defined"); return null; } + + private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; + private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; + private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; + + /** + * Return the number of available bytes at which the given path is + * considered running low on storage. + * + * @hide + */ + public long getStorageLowBytes(File path) { + final long lowPercent = Settings.Global.getInt(mResolver, + Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); + final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; + + final long maxLowBytes = Settings.Global.getLong(mResolver, + Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); + + return Math.min(lowBytes, maxLowBytes); + } + + /** + * Return the number of available bytes at which the given path is + * considered full. + * + * @hide + */ + public long getStorageFullBytes(File path) { + return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, + DEFAULT_FULL_THRESHOLD_BYTES); + } } diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 09ff7be..028317f 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -703,7 +703,13 @@ public abstract class PreferenceActivity extends ListActivity implements * show for the initial UI. */ public Header onGetInitialHeader() { - return mHeaders.get(0); + for (int i=0; i<mHeaders.size(); i++) { + Header h = mHeaders.get(i); + if (h.fragment != null) { + return h; + } + } + throw new IllegalStateException("Must have at least one header with a fragment"); } /** @@ -1167,6 +1173,9 @@ public abstract class PreferenceActivity extends ListActivity implements getFragmentManager().popBackStack(BACK_STACK_PREFS, FragmentManager.POP_BACK_STACK_INCLUSIVE); } else { + if (header.fragment == null) { + throw new IllegalStateException("can't switch to header that has no fragment"); + } int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader); switchToHeaderInner(header.fragment, header.fragmentArguments, direction); setSelectedHeader(header); diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index af6e88e9..2dd27f8 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -302,9 +302,16 @@ public final class CalendarContract { * Used to indicate that local, unsynced, changes are present. * <P>Type: INTEGER (long)</P> */ + public static final String DIRTY = "dirty"; /** + * Used in conjunction with {@link #DIRTY} to indicate what packages wrote local changes. + * <P>Type: TEXT</P> + */ + public static final String MUTATORS = "mutators"; + + /** * Whether the row has been deleted but not synced to the server. A * deleted row should be ignored. * <P> @@ -525,6 +532,7 @@ public final class CalendarContract { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, MUTATORS); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2); @@ -542,6 +550,8 @@ public final class CalendarContract { Calendars.CALENDAR_DISPLAY_NAME); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, + Calendars.CALENDAR_COLOR_KEY); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, CALENDAR_ACCESS_LEVEL); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS); @@ -647,6 +657,7 @@ public final class CalendarContract { * <li>{@link #CALENDAR_COLOR}</li> * <li>{@link #_SYNC_ID}</li> * <li>{@link #DIRTY}</li> + * <li>{@link #MUTATORS}</li> * <li>{@link #OWNER_ACCOUNT}</li> * <li>{@link #MAX_REMINDERS}</li> * <li>{@link #ALLOWED_REMINDERS}</li> @@ -714,6 +725,7 @@ public final class CalendarContract { ACCOUNT_TYPE, _SYNC_ID, DIRTY, + MUTATORS, OWNER_ACCOUNT, MAX_REMINDERS, ALLOWED_REMINDERS, @@ -1368,6 +1380,8 @@ public final class CalendarContract { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EVENT_COLOR); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_COLOR_KEY); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_ALARM); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_EXTENDED_PROPERTIES); @@ -1393,6 +1407,7 @@ public final class CalendarContract { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, IS_ORGANIZER); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, MUTATORS); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA1); @@ -1599,6 +1614,7 @@ public final class CalendarContract { * The following Events columns are writable only by a sync adapter * <ul> * <li>{@link #DIRTY}</li> + * <li>{@link #MUTATORS}</li> * <li>{@link #_SYNC_ID}</li> * <li>{@link #SYNC_DATA1}</li> * <li>{@link #SYNC_DATA2}</li> @@ -1688,6 +1704,7 @@ public final class CalendarContract { public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { _SYNC_ID, DIRTY, + MUTATORS, SYNC_DATA1, SYNC_DATA2, SYNC_DATA3, diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 31ad12b..9999760 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -407,6 +407,9 @@ public final class Downloads { */ public static final String COLUMN_LAST_UPDATESRC = "lastUpdateSrc"; + /** The column that is used to count retries */ + public static final String COLUMN_FAILED_CONNECTIONS = "numfailed"; + /** * default value for {@link #COLUMN_LAST_UPDATESRC}. * This value is used when this column's value is not relevant. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4dbc4b4..8715349 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -50,7 +50,6 @@ import android.speech.tts.TextToSpeech; import android.text.TextUtils; import android.util.AndroidException; import android.util.Log; -import android.view.WindowOrientationListener; import com.android.internal.widget.ILockSettings; @@ -456,6 +455,18 @@ public final class Settings { "android.settings.APPLICATION_DETAILS_SETTINGS"; /** + * @hide + * Activity Action: Show the "app ops" settings screen. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_APP_OPS_SETTINGS = + "android.settings.APP_OPS_SETTINGS"; + + /** * Activity Action: Show settings for system update functionality. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -774,7 +785,7 @@ public final class Settings { arg.putString(Settings.NameValueTable.VALUE, value); arg.putInt(CALL_METHOD_USER_KEY, userHandle); IContentProvider cp = lazyGetProvider(cr); - cp.call(mCallSetCommand, name, arg); + cp.call(cr.getPackageName(), mCallSetCommand, name, arg); } catch (RemoteException e) { Log.w(TAG, "Can't set key " + name + " in " + mUri, e); return false; @@ -821,7 +832,7 @@ public final class Settings { args = new Bundle(); args.putInt(CALL_METHOD_USER_KEY, userHandle); } - Bundle b = cp.call(mCallGetCommand, name, args); + Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); if (b != null) { String value = b.getPairValue(); // Don't update our cache for reads of other users' data @@ -846,7 +857,7 @@ public final class Settings { Cursor c = null; try { - c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER, + c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER, new String[]{name}, null, null); if (c == null) { Log.w(TAG, "Can't get key " + name + " from " + mUri); @@ -2610,6 +2621,7 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.ASSISTED_GPS_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.BLUETOOTH_ON); + MOVED_TO_GLOBAL.add(Settings.Global.BUGREPORT_IN_POWER_MENU); MOVED_TO_GLOBAL.add(Settings.Global.CDMA_CELL_BROADCAST_SMS); MOVED_TO_GLOBAL.add(Settings.Global.CDMA_ROAMING_MODE); MOVED_TO_GLOBAL.add(Settings.Global.CDMA_SUBSCRIPTION_MODE); @@ -2659,13 +2671,6 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.TETHER_DUN_APN); MOVED_TO_GLOBAL.add(Settings.Global.TETHER_DUN_REQUIRED); MOVED_TO_GLOBAL.add(Settings.Global.TETHER_SUPPORTED); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_HELP_URI); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_NOTIFICATION_TYPE); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_POLLING_SEC); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_RESET_DAY); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_THRESHOLD_BYTES); - MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_VALUE_KBITSPS); MOVED_TO_GLOBAL.add(Settings.Global.USB_MASS_STORAGE_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.USE_GOOGLE_MAIL); MOVED_TO_GLOBAL.add(Settings.Global.WEB_AUTOFILL_QUERY_URL); @@ -3081,8 +3086,10 @@ public final class Settings { /** * When the user has enable the option to have a "bug report" command * in the power menu. + * @deprecated Use {@link android.provider.Settings.Global#BUGREPORT_IN_POWER_MENU} instead * @hide */ + @Deprecated public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; /** @@ -4037,7 +4044,7 @@ public final class Settings { * @hide */ public static final String[] SETTINGS_TO_BACKUP = { - BUGREPORT_IN_POWER_MENU, + BUGREPORT_IN_POWER_MENU, // moved to global ALLOW_MOCK_LOCATION, PARENTAL_CONTROL_ENABLED, PARENTAL_CONTROL_REDIRECT_URL, @@ -4316,6 +4323,13 @@ public final class Settings { public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in"; /** + * When the user has enable the option to have a "bug report" command + * in the power menu. + * @hide + */ + public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; + + /** * Whether ADB is enabled. */ public static final String ADB_ENABLED = "adb_enabled"; @@ -4685,50 +4699,6 @@ public final class Settings { public static final String TETHER_DUN_APN = "tether_dun_apn"; /** - * The bandwidth throttle polling freqency in seconds - * @hide - */ - public static final String THROTTLE_POLLING_SEC = "throttle_polling_sec"; - - /** - * The bandwidth throttle threshold (long) - * @hide - */ - public static final String THROTTLE_THRESHOLD_BYTES = "throttle_threshold_bytes"; - - /** - * The bandwidth throttle value (kbps) - * @hide - */ - public static final String THROTTLE_VALUE_KBITSPS = "throttle_value_kbitsps"; - - /** - * The bandwidth throttle reset calendar day (1-28) - * @hide - */ - public static final String THROTTLE_RESET_DAY = "throttle_reset_day"; - - /** - * The throttling notifications we should send - * @hide - */ - public static final String THROTTLE_NOTIFICATION_TYPE = "throttle_notification_type"; - - /** - * Help URI for data throttling policy - * @hide - */ - public static final String THROTTLE_HELP_URI = "throttle_help_uri"; - - /** - * The length of time in Sec that we allow our notion of NTP time - * to be cached before we refresh it - * @hide - */ - public static final String THROTTLE_MAX_NTP_CACHE_AGE_SEC = - "throttle_max_ntp_cache_age_sec"; - - /** * USB Mass Storage Enabled */ public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; @@ -5359,6 +5329,7 @@ public final class Settings { * @hide */ public static final String[] SETTINGS_TO_BACKUP = { + BUGREPORT_IN_POWER_MENU, STAY_ON_WHILE_PLUGGED_IN, MODE_RINGER, AUTO_TIME, diff --git a/core/java/android/server/package.html b/core/java/android/server/package.html deleted file mode 100644 index c9f96a6..0000000 --- a/core/java/android/server/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<body> - -{@hide} - -</body> diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java deleted file mode 100644 index 1a10644..0000000 --- a/core/java/android/server/search/SearchManagerService.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.search; - -import com.android.internal.content.PackageMonitor; -import com.android.internal.util.IndentingPrintWriter; - -import android.app.ActivityManager; -import android.app.ActivityManagerNative; -import android.app.AppGlobals; -import android.app.ISearchManager; -import android.app.SearchManager; -import android.app.SearchableInfo; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.database.ContentObserver; -import android.os.Binder; -import android.os.Process; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; - -/** - * The search manager service handles the search UI, and maintains a registry of searchable - * activities. - */ -public class SearchManagerService extends ISearchManager.Stub { - - // general debugging support - private static final String TAG = "SearchManagerService"; - - // Context that the service is running in. - private final Context mContext; - - // This field is initialized lazily in getSearchables(), and then never modified. - private final SparseArray<Searchables> mSearchables = new SparseArray<Searchables>(); - - /** - * Initializes the Search Manager service in the provided system context. - * Only one instance of this object should be created! - * - * @param context to use for accessing DB, window manager, etc. - */ - public SearchManagerService(Context context) { - mContext = context; - mContext.registerReceiver(new BootCompletedReceiver(), - new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); - mContext.registerReceiver(new UserReceiver(), - new IntentFilter(Intent.ACTION_USER_REMOVED)); - new MyPackageMonitor().register(context, null, UserHandle.ALL, true); - } - - private Searchables getSearchables(int userId) { - long origId = Binder.clearCallingIdentity(); - try { - boolean userExists = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) - .getUserInfo(userId) != null; - if (!userExists) return null; - } finally { - Binder.restoreCallingIdentity(origId); - } - synchronized (mSearchables) { - Searchables searchables = mSearchables.get(userId); - - if (searchables == null) { - //Log.i(TAG, "Building list of searchable activities for userId=" + userId); - searchables = new Searchables(mContext, userId); - searchables.buildSearchableList(); - mSearchables.append(userId, searchables); - } - return searchables; - } - } - - private void onUserRemoved(int userId) { - if (userId != UserHandle.USER_OWNER) { - synchronized (mSearchables) { - mSearchables.remove(userId); - } - } - } - - /** - * Creates the initial searchables list after boot. - */ - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - new Thread() { - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - mContext.unregisterReceiver(BootCompletedReceiver.this); - getSearchables(0); - } - }.start(); - } - } - - private final class UserReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_OWNER)); - } - } - - /** - * Refreshes the "searchables" list when packages are added/removed. - */ - class MyPackageMonitor extends PackageMonitor { - - @Override - public void onSomePackagesChanged() { - updateSearchables(); - } - - @Override - public void onPackageModified(String pkg) { - updateSearchables(); - } - - private void updateSearchables() { - final int changingUserId = getChangingUserId(); - synchronized (mSearchables) { - // Update list of searchable activities - for (int i = 0; i < mSearchables.size(); i++) { - if (changingUserId == mSearchables.keyAt(i)) { - getSearchables(mSearchables.keyAt(i)).buildSearchableList(); - break; - } - } - } - // Inform all listeners that the list of searchables has been updated. - Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, new UserHandle(changingUserId)); - } - } - - class GlobalSearchProviderObserver extends ContentObserver { - private final ContentResolver mResolver; - - public GlobalSearchProviderObserver(ContentResolver resolver) { - super(null); - mResolver = resolver; - mResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY), - false /* notifyDescendants */, - this); - } - - @Override - public void onChange(boolean selfChange) { - synchronized (mSearchables) { - for (int i = 0; i < mSearchables.size(); i++) { - getSearchables(mSearchables.keyAt(i)).buildSearchableList(); - } - } - Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } - - } - - // - // Searchable activities API - // - - /** - * Returns the SearchableInfo for a given activity. - * - * @param launchActivity The activity from which we're launching this search. - * @return Returns a SearchableInfo record describing the parameters of the search, - * or null if no searchable metadata was available. - */ - public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { - if (launchActivity == null) { - Log.e(TAG, "getSearchableInfo(), activity == null"); - return null; - } - return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity); - } - - /** - * Returns a list of the searchable activities that can be included in global search. - */ - public List<SearchableInfo> getSearchablesInGlobalSearch() { - return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList(); - } - - public List<ResolveInfo> getGlobalSearchActivities() { - return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities(); - } - - /** - * Gets the name of the global search activity. - */ - public ComponentName getGlobalSearchActivity() { - return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity(); - } - - /** - * Gets the name of the web search activity. - */ - public ComponentName getWebSearchActivity() { - return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity(); - } - - @Override - public ComponentName getAssistIntent(int userHandle) { - try { - userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userHandle, true, false, "getAssistIntent", null); - IPackageManager pm = AppGlobals.getPackageManager(); - Intent assistIntent = new Intent(Intent.ACTION_ASSIST); - ResolveInfo info = - pm.resolveIntent(assistIntent, - assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()), - PackageManager.MATCH_DEFAULT_ONLY, userHandle); - if (info != null) { - return new ComponentName( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name); - } - } catch (RemoteException re) { - // Local call - Log.e(TAG, "RemoteException in getAssistIntent: " + re); - } catch (Exception e) { - Log.e(TAG, "Exception in getAssistIntent: " + e); - } - return null; - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - synchronized (mSearchables) { - for (int i = 0; i < mSearchables.size(); i++) { - ipw.print("\nUser: "); ipw.println(mSearchables.keyAt(i)); - ipw.increaseIndent(); - mSearchables.valueAt(i).dump(fd, ipw, args); - ipw.decreaseIndent(); - } - } - } -} diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java deleted file mode 100644 index a0095d6..0000000 --- a/core/java/android/server/search/Searchables.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.search; - -import android.app.AppGlobals; -import android.app.SearchManager; -import android.app.SearchableInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Binder; -import android.os.Bundle; -import android.os.RemoteException; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; - -/** - * This class maintains the information about all searchable activities. - * This is a hidden class. - */ -public class Searchables { - - private static final String LOG_TAG = "Searchables"; - - // static strings used for XML lookups, etc. - // TODO how should these be documented for the developer, in a more structured way than - // the current long wordy javadoc in SearchManager.java ? - private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable"; - private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*"; - - private Context mContext; - - private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; - private ArrayList<SearchableInfo> mSearchablesList = null; - private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; - // Contains all installed activities that handle the global search - // intent. - private List<ResolveInfo> mGlobalSearchActivities; - private ComponentName mCurrentGlobalSearchActivity = null; - private ComponentName mWebSearchActivity = null; - - public static String GOOGLE_SEARCH_COMPONENT_NAME = - "com.android.googlesearch/.GoogleSearch"; - public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME = - "com.google.android.providers.enhancedgooglesearch/.Launcher"; - - // Cache the package manager instance - final private IPackageManager mPm; - // User for which this Searchables caches information - private int mUserId; - - /** - * - * @param context Context to use for looking up activities etc. - */ - public Searchables (Context context, int userId) { - mContext = context; - mUserId = userId; - mPm = AppGlobals.getPackageManager(); - } - - /** - * Look up, or construct, based on the activity. - * - * The activities fall into three cases, based on meta-data found in - * the manifest entry: - * <ol> - * <li>The activity itself implements search. This is indicated by the - * presence of a "android.app.searchable" meta-data attribute. - * The value is a reference to an XML file containing search information.</li> - * <li>A related activity implements search. This is indicated by the - * presence of a "android.app.default_searchable" meta-data attribute. - * The value is a string naming the activity implementing search. In this - * case the factory will "redirect" and return the searchable data.</li> - * <li>No searchability data is provided. We return null here and other - * code will insert the "default" (e.g. contacts) search. - * - * TODO: cache the result in the map, and check the map first. - * TODO: it might make sense to implement the searchable reference as - * an application meta-data entry. This way we don't have to pepper each - * and every activity. - * TODO: can we skip the constructor step if it's a non-searchable? - * TODO: does it make sense to plug the default into a slot here for - * automatic return? Probably not, but it's one way to do it. - * - * @param activity The name of the current activity, or null if the - * activity does not define any explicit searchable metadata. - */ - public SearchableInfo getSearchableInfo(ComponentName activity) { - // Step 1. Is the result already hashed? (case 1) - SearchableInfo result; - synchronized (this) { - result = mSearchablesMap.get(activity); - if (result != null) return result; - } - - // Step 2. See if the current activity references a searchable. - // Note: Conceptually, this could be a while(true) loop, but there's - // no point in implementing reference chaining here and risking a loop. - // References must point directly to searchable activities. - - ActivityInfo ai = null; - try { - ai = mPm.getActivityInfo(activity, PackageManager.GET_META_DATA, mUserId); - } catch (RemoteException re) { - Log.e(LOG_TAG, "Error getting activity info " + re); - return null; - } - String refActivityName = null; - - // First look for activity-specific reference - Bundle md = ai.metaData; - if (md != null) { - refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE); - } - // If not found, try for app-wide reference - if (refActivityName == null) { - md = ai.applicationInfo.metaData; - if (md != null) { - refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE); - } - } - - // Irrespective of source, if a reference was found, follow it. - if (refActivityName != null) - { - // This value is deprecated, return null - if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { - return null; - } - String pkg = activity.getPackageName(); - ComponentName referredActivity; - if (refActivityName.charAt(0) == '.') { - referredActivity = new ComponentName(pkg, pkg + refActivityName); - } else { - referredActivity = new ComponentName(pkg, refActivityName); - } - - // Now try the referred activity, and if found, cache - // it against the original name so we can skip the check - synchronized (this) { - result = mSearchablesMap.get(referredActivity); - if (result != null) { - mSearchablesMap.put(activity, result); - return result; - } - } - } - - // Step 3. None found. Return null. - return null; - - } - - /** - * Builds an entire list (suitable for display) of - * activities that are searchable, by iterating the entire set of - * ACTION_SEARCH & ACTION_WEB_SEARCH intents. - * - * Also clears the hash of all activities -> searches which will - * refill as the user clicks "search". - * - * This should only be done at startup and again if we know that the - * list has changed. - * - * TODO: every activity that provides a ACTION_SEARCH intent should - * also provide searchability meta-data. There are a bunch of checks here - * that, if data is not found, silently skip to the next activity. This - * won't help a developer trying to figure out why their activity isn't - * showing up in the list, but an exception here is too rough. I would - * like to find a better notification mechanism. - * - * TODO: sort the list somehow? UI choice. - */ - public void buildSearchableList() { - // These will become the new values at the end of the method - HashMap<ComponentName, SearchableInfo> newSearchablesMap - = new HashMap<ComponentName, SearchableInfo>(); - ArrayList<SearchableInfo> newSearchablesList - = new ArrayList<SearchableInfo>(); - ArrayList<SearchableInfo> newSearchablesInGlobalSearchList - = new ArrayList<SearchableInfo>(); - - // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. - List<ResolveInfo> searchList; - final Intent intent = new Intent(Intent.ACTION_SEARCH); - - long ident = Binder.clearCallingIdentity(); - try { - searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA); - - List<ResolveInfo> webSearchInfoList; - final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); - webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); - - // analyze each one, generate a Searchables record, and record - if (searchList != null || webSearchInfoList != null) { - int search_count = (searchList == null ? 0 : searchList.size()); - int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size()); - int count = search_count + web_search_count; - for (int ii = 0; ii < count; ii++) { - // for each component, try to find metadata - ResolveInfo info = (ii < search_count) - ? searchList.get(ii) - : webSearchInfoList.get(ii - search_count); - ActivityInfo ai = info.activityInfo; - // Check first to avoid duplicate entries. - if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) { - SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai, - mUserId); - if (searchable != null) { - newSearchablesList.add(searchable); - newSearchablesMap.put(searchable.getSearchActivity(), searchable); - if (searchable.shouldIncludeInGlobalSearch()) { - newSearchablesInGlobalSearchList.add(searchable); - } - } - } - } - } - - List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities(); - - // Find the global search activity - ComponentName newGlobalSearchActivity = findGlobalSearchActivity( - newGlobalSearchActivities); - - // Find the web search activity - ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity); - - // Store a consistent set of new values - synchronized (this) { - mSearchablesMap = newSearchablesMap; - mSearchablesList = newSearchablesList; - mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; - mGlobalSearchActivities = newGlobalSearchActivities; - mCurrentGlobalSearchActivity = newGlobalSearchActivity; - mWebSearchActivity = newWebSearchActivity; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - /** - * Returns a sorted list of installed search providers as per - * the following heuristics: - * - * (a) System apps are given priority over non system apps. - * (b) Among system apps and non system apps, the relative ordering - * is defined by their declared priority. - */ - private List<ResolveInfo> findGlobalSearchActivities() { - // Step 1 : Query the package manager for a list - // of activities that can handle the GLOBAL_SEARCH intent. - Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); - List<ResolveInfo> activities = - queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - if (activities != null && !activities.isEmpty()) { - // Step 2: Rank matching activities according to our heuristics. - Collections.sort(activities, GLOBAL_SEARCH_RANKER); - } - - return activities; - } - - /** - * Finds the global search activity. - */ - private ComponentName findGlobalSearchActivity(List<ResolveInfo> installed) { - // Fetch the global search provider from the system settings, - // and if it's still installed, return it. - final String searchProviderSetting = getGlobalSearchProviderSetting(); - if (!TextUtils.isEmpty(searchProviderSetting)) { - final ComponentName globalSearchComponent = ComponentName.unflattenFromString( - searchProviderSetting); - if (globalSearchComponent != null && isInstalled(globalSearchComponent)) { - return globalSearchComponent; - } - } - - return getDefaultGlobalSearchProvider(installed); - } - - /** - * Checks whether the global search provider with a given - * component name is installed on the system or not. This deals with - * cases such as the removal of an installed provider. - */ - private boolean isInstalled(ComponentName globalSearch) { - Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); - intent.setComponent(globalSearch); - - List<ResolveInfo> activities = queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY); - if (activities != null && !activities.isEmpty()) { - return true; - } - - return false; - } - - private static final Comparator<ResolveInfo> GLOBAL_SEARCH_RANKER = - new Comparator<ResolveInfo>() { - @Override - public int compare(ResolveInfo lhs, ResolveInfo rhs) { - if (lhs == rhs) { - return 0; - } - boolean lhsSystem = isSystemApp(lhs); - boolean rhsSystem = isSystemApp(rhs); - - if (lhsSystem && !rhsSystem) { - return -1; - } else if (rhsSystem && !lhsSystem) { - return 1; - } else { - // Either both system engines, or both non system - // engines. - // - // Note, this isn't a typo. Higher priority numbers imply - // higher priority, but are "lower" in the sort order. - return rhs.priority - lhs.priority; - } - } - }; - - /** - * @return true iff. the resolve info corresponds to a system application. - */ - private static final boolean isSystemApp(ResolveInfo res) { - return (res.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } - - /** - * Returns the highest ranked search provider as per the - * ranking defined in {@link #getGlobalSearchActivities()}. - */ - private ComponentName getDefaultGlobalSearchProvider(List<ResolveInfo> providerList) { - if (providerList != null && !providerList.isEmpty()) { - ActivityInfo ai = providerList.get(0).activityInfo; - return new ComponentName(ai.packageName, ai.name); - } - - Log.w(LOG_TAG, "No global search activity found"); - return null; - } - - private String getGlobalSearchProviderSetting() { - return Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY); - } - - /** - * Finds the web search activity. - * - * Only looks in the package of the global search activity. - */ - private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) { - if (globalSearchActivity == null) { - return null; - } - Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); - intent.setPackage(globalSearchActivity.getPackageName()); - List<ResolveInfo> activities = - queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - - if (activities != null && !activities.isEmpty()) { - ActivityInfo ai = activities.get(0).activityInfo; - // TODO: do some sanity checks here? - return new ComponentName(ai.packageName, ai.name); - } - Log.w(LOG_TAG, "No web search activity found"); - return null; - } - - private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { - List<ResolveInfo> activities = null; - try { - activities = - mPm.queryIntentActivities(intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - flags, mUserId); - } catch (RemoteException re) { - // Local call - } - return activities; - } - - /** - * Returns the list of searchable activities. - */ - public synchronized ArrayList<SearchableInfo> getSearchablesList() { - ArrayList<SearchableInfo> result = new ArrayList<SearchableInfo>(mSearchablesList); - return result; - } - - /** - * Returns a list of the searchable activities that can be included in global search. - */ - public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() { - return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList); - } - - /** - * Returns a list of activities that handle the global search intent. - */ - public synchronized ArrayList<ResolveInfo> getGlobalSearchActivities() { - return new ArrayList<ResolveInfo>(mGlobalSearchActivities); - } - - /** - * Gets the name of the global search activity. - */ - public synchronized ComponentName getGlobalSearchActivity() { - return mCurrentGlobalSearchActivity; - } - - /** - * Gets the name of the web search activity. - */ - public synchronized ComponentName getWebSearchActivity() { - return mWebSearchActivity; - } - - void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("Searchable authorities:"); - synchronized (this) { - if (mSearchablesList != null) { - for (SearchableInfo info: mSearchablesList) { - pw.print(" "); pw.println(info.getSuggestAuthority()); - } - } - } - } -} diff --git a/core/java/android/server/search/package.html b/core/java/android/server/search/package.html deleted file mode 100644 index c9f96a6..0000000 --- a/core/java/android/server/search/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<body> - -{@hide} - -</body> diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index d1b23e4..9dc77b9 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -84,7 +84,7 @@ public abstract class WallpaperService extends Service { * tag. */ public static final String SERVICE_META_DATA = "android.service.wallpaper"; - + static final String TAG = "WallpaperService"; static final boolean DEBUG = false; @@ -100,7 +100,6 @@ public abstract class WallpaperService extends Service { private static final int MSG_WINDOW_MOVED = 10035; private static final int MSG_TOUCH_EVENT = 10040; - private Looper mCallbackLooper; private final ArrayList<Engine> mActiveEngines = new ArrayList<Engine>(); @@ -1099,13 +1098,14 @@ public abstract class WallpaperService extends Service { mTarget = context; } + @Override public void attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight) { new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType, isPreview, reqWidth, reqHeight); } } - + @Override public void onCreate() { super.onCreate(); @@ -1128,20 +1128,7 @@ public abstract class WallpaperService extends Service { public final IBinder onBind(Intent intent) { return new IWallpaperServiceWrapper(this); } - - /** - * This allows subclasses to change the thread that most callbacks - * occur on. Currently hidden because it is mostly needed for the - * image wallpaper (which runs in the system process and doesn't want - * to get stuck running on that seriously in use main thread). Not - * exposed right now because the semantics of this are not totally - * well defined and some callbacks can still happen on the main thread). - * @hide - */ - public void setCallbackLooper(Looper looper) { - mCallbackLooper = looper; - } - + /** * Must be implemented to return a new instance of the wallpaper's engine. * Note that multiple instances may be active at the same time, such as diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java index 3e33e8e..ab8f82f 100644 --- a/core/java/android/speech/tts/FileSynthesisCallback.java +++ b/core/java/android/speech/tts/FileSynthesisCallback.java @@ -20,10 +20,12 @@ import android.os.FileUtils; import android.util.Log; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.channels.FileChannel; /** * Speech synthesis request that writes the audio to a WAV file. @@ -39,16 +41,19 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { private static final short WAV_FORMAT_PCM = 0x0001; private final Object mStateLock = new Object(); - private final File mFileName; + private int mSampleRateInHz; private int mAudioFormat; private int mChannelCount; - private RandomAccessFile mFile; + + private FileChannel mFileChannel; + + private boolean mStarted = false; private boolean mStopped = false; private boolean mDone = false; - FileSynthesisCallback(File fileName) { - mFileName = fileName; + FileSynthesisCallback(FileChannel fileChannel) { + mFileChannel = fileChannel; } @Override @@ -63,54 +68,23 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { * Must be called while holding the monitor on {@link #mStateLock}. */ private void cleanUp() { - closeFileAndWidenPermissions(); - if (mFile != null) { - mFileName.delete(); - } + closeFile(); } /** * Must be called while holding the monitor on {@link #mStateLock}. */ - private void closeFileAndWidenPermissions() { + private void closeFile() { try { - if (mFile != null) { - mFile.close(); - mFile = null; + if (mFileChannel != null) { + mFileChannel.close(); + mFileChannel = null; } } catch (IOException ex) { - Log.e(TAG, "Failed to close " + mFileName + ": " + ex); - } - - try { - // Make the written file readable and writeable by everyone. - // This allows the app that requested synthesis to read the file. - // - // Note that the directory this file was written must have already - // been world writeable in order it to have been - // written to in the first place. - FileUtils.setPermissions(mFileName.getAbsolutePath(), 0666, -1, -1); //-rw-rw-rw - } catch (SecurityException se) { - Log.e(TAG, "Security exception setting rw permissions on : " + mFileName); - } - } - - /** - * Checks whether a given file exists, and deletes it if it does. - */ - private boolean maybeCleanupExistingFile(File file) { - if (file.exists()) { - Log.v(TAG, "File " + file + " exists, deleting."); - if (!file.delete()) { - Log.e(TAG, "Failed to delete " + file); - return false; - } + Log.e(TAG, "Failed to close output file descriptor", ex); } - - return true; } - @Override public int getMaxBufferSize() { return MAX_AUDIO_BUFFER_SIZE; @@ -132,25 +106,20 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; } - if (mFile != null) { + if (mStarted) { cleanUp(); throw new IllegalArgumentException("FileSynthesisRequest.start() called twice"); } - - if (!maybeCleanupExistingFile(mFileName)) { - return TextToSpeech.ERROR; - } - + mStarted = true; mSampleRateInHz = sampleRateInHz; mAudioFormat = audioFormat; mChannelCount = channelCount; + try { - mFile = new RandomAccessFile(mFileName, "rw"); - // Reserve space for WAV header - mFile.write(new byte[WAV_HEADER_LENGTH]); + mFileChannel.write(ByteBuffer.allocate(WAV_HEADER_LENGTH)); return TextToSpeech.SUCCESS; } catch (IOException ex) { - Log.e(TAG, "Failed to open " + mFileName + ": " + ex); + Log.e(TAG, "Failed to write wav header to output file descriptor" + ex); cleanUp(); return TextToSpeech.ERROR; } @@ -168,15 +137,15 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; } - if (mFile == null) { + if (mFileChannel == null) { Log.e(TAG, "File not open"); return TextToSpeech.ERROR; } try { - mFile.write(buffer, offset, length); + mFileChannel.write(ByteBuffer.wrap(buffer, offset, length)); return TextToSpeech.SUCCESS; } catch (IOException ex) { - Log.e(TAG, "Failed to write to " + mFileName + ": " + ex); + Log.e(TAG, "Failed to write to output file descriptor", ex); cleanUp(); return TextToSpeech.ERROR; } @@ -197,21 +166,21 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; } - if (mFile == null) { + if (mFileChannel == null) { Log.e(TAG, "File not open"); return TextToSpeech.ERROR; } try { // Write WAV header at start of file - mFile.seek(0); - int dataLength = (int) (mFile.length() - WAV_HEADER_LENGTH); - mFile.write( + mFileChannel.position(0); + int dataLength = (int) (mFileChannel.size() - WAV_HEADER_LENGTH); + mFileChannel.write( makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength)); - closeFileAndWidenPermissions(); + closeFile(); mDone = true; return TextToSpeech.SUCCESS; } catch (IOException ex) { - Log.e(TAG, "Failed to write to " + mFileName + ": " + ex); + Log.e(TAG, "Failed to write to output file descriptor", ex); cleanUp(); return TextToSpeech.ERROR; } @@ -226,7 +195,7 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { } } - private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount, + private ByteBuffer makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount, int dataLength) { // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT? int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); @@ -251,8 +220,9 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { header.putShort(bitsPerSample); header.put(new byte[]{ 'd', 'a', 't', 'a' }); header.putInt(dataLength); + header.flip(); - return headerBuf; + return header; } } diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl index ab63187..b7bc70c 100644 --- a/core/java/android/speech/tts/ITextToSpeechService.aidl +++ b/core/java/android/speech/tts/ITextToSpeechService.aidl @@ -18,6 +18,7 @@ package android.speech.tts; import android.net.Uri; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.speech.tts.ITextToSpeechCallback; /** @@ -44,11 +45,12 @@ interface ITextToSpeechService { * @param callingInstance a binder representing the identity of the calling * TextToSpeech object. * @param text The text to synthesize. - * @param filename The file to write the synthesized audio to. + * @param fileDescriptor The file descriptor to write the synthesized audio to. Has to be + writable. * @param param Request parameters. */ - int synthesizeToFile(in IBinder callingInstance, in String text, - in String filename, in Bundle params); + int synthesizeToFileDescriptor(in IBinder callingInstance, in String text, + in ParcelFileDescriptor fileDescriptor, in Bundle params); /** * Plays an existing audio resource. @@ -97,7 +99,19 @@ interface ITextToSpeechService { * be empty too. */ String[] getLanguage(); - + + /** + * Returns a default TTS language, country and variant as set by the user. + * + * Can be called from multiple threads. + * + * @return A 3-element array, containing language (ISO 3-letter code), + * country (ISO 3-letter code) and variant used by the engine. + * The country and variant may be {@code ""}. If country is empty, then variant must + * be empty too. + */ + String[] getClientDefaultLanguage(); + /** * Checks whether the engine supports a given language. * @@ -131,6 +145,8 @@ interface ITextToSpeechService { /** * Notifies the engine that it should load a speech synthesis language. * + * @param caller a binder representing the identity of the calling + * TextToSpeech object. * @param lang ISO-3 language code. * @param country ISO-3 country code. May be empty or null. * @param variant Language variant. May be empty or null. @@ -141,13 +157,14 @@ interface ITextToSpeechService { * {@link TextToSpeech#LANG_MISSING_DATA} * {@link TextToSpeech#LANG_NOT_SUPPORTED}. */ - int loadLanguage(in String lang, in String country, in String variant); + int loadLanguage(in IBinder caller, in String lang, in String country, in String variant); /** * Sets the callback that will be notified when playback of utterance from the * given app are completed. * - * @param callingApp Package name for the app whose utterance the callback will handle. + * @param caller Instance a binder representing the identity of the calling + * TextToSpeech object. * @param cb The callback. */ void setCallback(in IBinder caller, ITextToSpeechCallback cb); diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java index d70c371..f98bb09 100644 --- a/core/java/android/speech/tts/SynthesisCallback.java +++ b/core/java/android/speech/tts/SynthesisCallback.java @@ -22,10 +22,11 @@ package android.speech.tts; * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally * {@link #done}. * - * * {@link #error} can be called at any stage in the synthesis process to * indicate that an error has occurred, but if the call is made after a call * to {@link #done}, it might be discarded. + * + * After {@link #start} been called, {@link #done} must be called regardless of errors. */ public interface SynthesisCallback { /** @@ -72,6 +73,8 @@ public interface SynthesisCallback { * This method should only be called on the synthesis thread, * while in {@link TextToSpeechService#onSynthesizeText}. * + * This method has to be called if {@link #start} was called. + * * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. */ public int done(); diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 5e367cb..73d400e 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -24,13 +24,18 @@ import android.content.Intent; import android.content.ServiceConnection; import android.media.AudioManager; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -139,7 +144,10 @@ public class TextToSpeech { * Listener that will be called when the TTS service has * completed synthesizing an utterance. This is only called if the utterance * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}). + * + * @deprecated Use {@link UtteranceProgressListener} instead. */ + @Deprecated public interface OnUtteranceCompletedListener { /** * Called when an utterance has been synthesized. @@ -235,19 +243,28 @@ public class TextToSpeech { /** * Indicates erroneous data when checking the installation status of the resources used by * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. + * + * @deprecated Use CHECK_VOICE_DATA_FAIL instead. */ + @Deprecated public static final int CHECK_VOICE_DATA_BAD_DATA = -1; /** * Indicates missing resources when checking the installation status of the resources used * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. + * + * @deprecated Use CHECK_VOICE_DATA_FAIL instead. */ + @Deprecated public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; /** * Indicates missing storage volume when checking the installation status of the resources * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. + * + * @deprecated Use CHECK_VOICE_DATA_FAIL instead. */ + @Deprecated public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3; /** @@ -283,9 +300,8 @@ public class TextToSpeech { "android.speech.tts.engine.INSTALL_TTS_DATA"; /** - * Broadcast Action: broadcast to signal the completion of the installation of - * the data files used by the synthesis engine. Success or failure is indicated in the - * {@link #EXTRA_TTS_DATA_INSTALLED} extra. + * Broadcast Action: broadcast to signal the change in the list of available + * languages or/and their features. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_TTS_DATA_INSTALLED = @@ -298,20 +314,16 @@ public class TextToSpeech { * return one of the following codes: * {@link #CHECK_VOICE_DATA_PASS}, * {@link #CHECK_VOICE_DATA_FAIL}, - * {@link #CHECK_VOICE_DATA_BAD_DATA}, - * {@link #CHECK_VOICE_DATA_MISSING_DATA}, or - * {@link #CHECK_VOICE_DATA_MISSING_VOLUME}. * <p> Moreover, the data received in the activity result will contain the following * fields: * <ul> - * <li>{@link #EXTRA_VOICE_DATA_ROOT_DIRECTORY} which - * indicates the path to the location of the resource files,</li> - * <li>{@link #EXTRA_VOICE_DATA_FILES} which contains - * the list of all the resource files,</li> - * <li>and {@link #EXTRA_VOICE_DATA_FILES_INFO} which - * contains, for each resource file, the description of the language covered by - * the file in the xxx-YYY format, where xxx is the 3-letter ISO language code, - * and YYY is the 3-letter ISO country code.</li> + * <li>{@link #EXTRA_AVAILABLE_VOICES} which contains an ArrayList<String> of all the + * available voices. The format of each voice is: lang-COUNTRY-variant where COUNTRY and + * variant are optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").</li> + * <li>{@link #EXTRA_UNAVAILABLE_VOICES} which contains an ArrayList<String> of all the + * unavailable voices (ones that user can install). The format of each voice is: + * lang-COUNTRY-variant where COUNTRY and variant are optional (ie, "eng" or + * "eng-USA" or "eng-USA-FEMALE").</li> * </ul> */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -319,37 +331,33 @@ public class TextToSpeech { "android.speech.tts.engine.CHECK_TTS_DATA"; /** - * Activity intent for getting some sample text to use for demonstrating TTS. + * Activity intent for getting some sample text to use for demonstrating TTS. Specific + * locale have to be requested by passing following extra parameters: + * <ul> + * <li>language</li> + * <li>country</li> + * <li>variant</li> + * </ul> * - * @hide This intent was used by engines written against the old API. - * Not sure if it should be exposed. + * Upon completion, the activity result may contain the following fields: + * <ul> + * <li>{@link #EXTRA_SAMPLE_TEXT} which contains an String with sample text.</li> + * </ul> */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_GET_SAMPLE_TEXT = "android.speech.tts.engine.GET_SAMPLE_TEXT"; - // extras for a TTS engine's check data activity /** - * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where - * the TextToSpeech engine specifies the path to its resources. + * Extra information received with the {@link #ACTION_GET_SAMPLE_TEXT} intent result where + * the TextToSpeech engine returns an String with sample text for requested voice */ - public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot"; + public static final String EXTRA_SAMPLE_TEXT = "sampleText"; - /** - * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where - * the TextToSpeech engine specifies the file names of its resources under the - * resource path. - */ - public static final String EXTRA_VOICE_DATA_FILES = "dataFiles"; - - /** - * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where - * the TextToSpeech engine specifies the locale associated with each resource file. - */ - public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo"; + // extras for a TTS engine's check data activity /** - * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where + * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where * the TextToSpeech engine returns an ArrayList<String> of all the available voices. * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). @@ -357,7 +365,7 @@ public class TextToSpeech { public static final String EXTRA_AVAILABLE_VOICES = "availableVoices"; /** - * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where + * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices. * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). @@ -365,22 +373,63 @@ public class TextToSpeech { public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices"; /** + * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where + * the TextToSpeech engine specifies the path to its resources. + * + * It may be used by language packages to find out where to put their data. + * + * @deprecated TTS engine implementation detail, this information has no use for + * text-to-speech API client. + */ + @Deprecated + public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot"; + + /** + * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where + * the TextToSpeech engine specifies the file names of its resources under the + * resource path. + * + * @deprecated TTS engine implementation detail, this information has no use for + * text-to-speech API client. + */ + @Deprecated + public static final String EXTRA_VOICE_DATA_FILES = "dataFiles"; + + /** + * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where + * the TextToSpeech engine specifies the locale associated with each resource file. + * + * @deprecated TTS engine implementation detail, this information has no use for + * text-to-speech API client. + */ + @Deprecated + public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo"; + + /** * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the * caller indicates to the TextToSpeech engine which specific sets of voice data to * check for by sending an ArrayList<String> of the voices that are of interest. * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). + * + * @deprecated Redundant functionality, checking for existence of specific sets of voice + * data can be done on client side. */ + @Deprecated public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor"; // extras for a TTS engine's data installation /** - * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent. + * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent result. * It indicates whether the data files for the synthesis engine were successfully * installed. The installation was initiated with the {@link #ACTION_INSTALL_TTS_DATA} * intent. The possible values for this extra are * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}. + * + * @deprecated No longer in use. If client ise interested in information about what + * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices. */ + @Deprecated public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled"; // keys for the parameters passed with speak commands. Hidden keys are used internally @@ -473,11 +522,16 @@ public class TextToSpeech { * for a description of how feature keys work. If set and supported by the engine * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize * text on-device (without making network requests). + * + * @see TextToSpeech#speak(String, int, java.util.HashMap) + * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String) + * @see TextToSpeech#getFeatures(java.util.Locale) */ public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts"; } private final Context mContext; + private Connection mConnectingServiceConnection; private Connection mServiceConnection; private OnInitListener mInitListener; // Written from an unspecified application thread, read from @@ -553,21 +607,24 @@ public class TextToSpeech { initTts(); } - private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) { - return runAction(action, errorResult, method, false); + private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method, + boolean onlyEstablishedConnection) { + return runAction(action, errorResult, method, false, onlyEstablishedConnection); } private <R> R runAction(Action<R> action, R errorResult, String method) { - return runAction(action, errorResult, method, true); + return runAction(action, errorResult, method, true, true); } - private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) { + private <R> R runAction(Action<R> action, R errorResult, String method, + boolean reconnect, boolean onlyEstablishedConnection) { synchronized (mStartLock) { if (mServiceConnection == null) { Log.w(TAG, method + " failed: not bound to TTS engine"); return errorResult; } - return mServiceConnection.runAction(action, errorResult, method, reconnect); + return mServiceConnection.runAction(action, errorResult, method, reconnect, + onlyEstablishedConnection); } } @@ -630,6 +687,7 @@ public class TextToSpeech { return false; } else { Log.i(TAG, "Sucessfully bound to " + engine); + mConnectingServiceConnection = connection; return true; } } @@ -653,6 +711,16 @@ public class TextToSpeech { * so the TextToSpeech engine can be cleanly stopped. */ public void shutdown() { + // Special case, we are asked to shutdown connection that did finalize its connection. + synchronized (mStartLock) { + if (mConnectingServiceConnection != null) { + mContext.unbindService(mConnectingServiceConnection); + mConnectingServiceConnection = null; + return; + } + } + + // Post connection case runActionNoReconnect(new Action<Void>() { @Override public Void run(ITextToSpeechService service) throws RemoteException { @@ -670,7 +738,7 @@ public class TextToSpeech { mCurrentEngine = null; return null; } - }, null, "shutdown"); + }, null, "shutdown", false); } /** @@ -793,10 +861,16 @@ public class TextToSpeech { } /** - * Speaks the string using the specified queuing strategy and speech - * parameters. + * Speaks the string using the specified queuing strategy and speech parameters. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * - * @param text The string of text to be spoken. + * @param text The string of text to be spoken. No longer than + * {@link #getMaxSpeechInputLength()} characters. * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. * @param params Parameters for the request. Can be null. * Supported parameter names: @@ -809,7 +883,7 @@ public class TextToSpeech { * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. */ public int speak(final String text, final int queueMode, final HashMap<String, String> params) { return runAction(new Action<Integer>() { @@ -830,6 +904,12 @@ public class TextToSpeech { * Plays the earcon using the specified queueing mode and parameters. * The earcon must already have been added with {@link #addEarcon(String, String)} or * {@link #addEarcon(String, String, int)}. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * * @param earcon The earcon that should be played * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. @@ -842,7 +922,7 @@ public class TextToSpeech { * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. */ public int playEarcon(final String earcon, final int queueMode, final HashMap<String, String> params) { @@ -862,6 +942,12 @@ public class TextToSpeech { /** * Plays silence for the specified amount of time using the specified * queue mode. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * * @param durationInMs The duration of the silence. * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. @@ -873,7 +959,7 @@ public class TextToSpeech { * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation. */ public int playSilence(final long durationInMs, final int queueMode, final HashMap<String, String> params) { @@ -1005,6 +1091,24 @@ public class TextToSpeech { } /** + * Returns a Locale instance describing the language currently being used as the default + * Text-to-speech language. + * + * @return language, country (if any) and variant (if any) used by the client stored in a + * Locale instance, or {@code null} on error. + */ + public Locale getDefaultLanguage() { + return runAction(new Action<Locale>() { + @Override + public Locale run(ITextToSpeechService service) throws RemoteException { + String[] defaultLanguage = service.getClientDefaultLanguage(); + + return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]); + } + }, null, "getDefaultLanguage"); + } + + /** * Sets the text-to-speech language. * The TTS engine will try to use the closest match to the specified * language as represented by the Locale, but there is no guarantee that the exact same Locale @@ -1031,7 +1135,8 @@ public class TextToSpeech { // the available parts. // Note that the language is not actually set here, instead it is cached so it // will be associated with all upcoming utterances. - int result = service.loadLanguage(language, country, variant); + + int result = service.loadLanguage(getCallerIdentity(), language, country, variant); if (result >= LANG_AVAILABLE){ if (result < LANG_COUNTRY_VAR_AVAILABLE) { variant = ""; @@ -1049,21 +1154,30 @@ public class TextToSpeech { } /** - * Returns a Locale instance describing the language currently being used by the TextToSpeech - * engine. + * Returns a Locale instance describing the language currently being used for synthesis + * requests sent to the TextToSpeech engine. + * + * In Android 4.2 and before (API <= 17) this function returns the language that is currently + * being used by the TTS engine. That is the last language set by this or any other + * client by a {@link TextToSpeech#setLanguage} call to the same engine. * - * @return language, country (if any) and variant (if any) used by the engine stored in a Locale - * instance, or {@code null} on error. + * In Android versions after 4.2 this function returns the language that is currently being + * used for the synthesis requests sent from this client. That is the last language set + * by a {@link TextToSpeech#setLanguage} call on this instance. + * + * @return language, country (if any) and variant (if any) used by the client stored in a + * Locale instance, or {@code null} on error. */ public Locale getLanguage() { return runAction(new Action<Locale>() { @Override - public Locale run(ITextToSpeechService service) throws RemoteException { - String[] locStrings = service.getLanguage(); - if (locStrings != null && locStrings.length == 3) { - return new Locale(locStrings[0], locStrings[1], locStrings[2]); - } - return null; + public Locale run(ITextToSpeechService service) { + /* No service call, but we're accessing mParams, hence need for + wrapping it as an Action instance */ + String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, ""); + String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, ""); + String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, ""); + return new Locale(lang, country, variant); } }, null, "getLanguage"); } @@ -1089,8 +1203,15 @@ public class TextToSpeech { /** * Synthesizes the given text to a file using the specified parameters. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * - * @param text The text that should be synthesized + * @param text The text that should be synthesized. No longer than + * {@link #getMaxSpeechInputLength()} characters. * @param params Parameters for the request. Can be null. * Supported parameter names: * {@link Engine#KEY_PARAM_UTTERANCE_ID}. @@ -1101,15 +1222,36 @@ public class TextToSpeech { * @param filename Absolute file filename to write the generated audio data to.It should be * something like "/sdcard/myappsounds/mysound.wav". * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. */ public int synthesizeToFile(final String text, final HashMap<String, String> params, final String filename) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { - return service.synthesizeToFile(getCallerIdentity(), text, filename, - getParams(params)); + ParcelFileDescriptor fileDescriptor; + int returnValue; + try { + File file = new File(filename); + if(file.exists() && !file.canWrite()) { + Log.e(TAG, "Can't write to " + filename); + return ERROR; + } + fileDescriptor = ParcelFileDescriptor.open(file, + ParcelFileDescriptor.MODE_WRITE_ONLY | + ParcelFileDescriptor.MODE_CREATE | + ParcelFileDescriptor.MODE_TRUNCATE); + returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text, + fileDescriptor, getParams(params)); + fileDescriptor.close(); + return returnValue; + } catch (FileNotFoundException e) { + Log.e(TAG, "Opening file " + filename + " failed", e); + return ERROR; + } catch (IOException e) { + Log.e(TAG, "Closing file " + filename + " failed", e); + return ERROR; + } } }, ERROR, "synthesizeToFile"); } @@ -1253,9 +1395,13 @@ public class TextToSpeech { return mEnginesHelper.getEngines(); } - private class Connection implements ServiceConnection { private ITextToSpeechService mService; + + private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask; + + private boolean mEstablished; + private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { @Override public void onDone(String utteranceId) { @@ -1282,23 +1428,66 @@ public class TextToSpeech { } }; + private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> { + private final ComponentName mName; + + public SetupConnectionAsyncTask(ComponentName name) { + mName = name; + } + + @Override + protected Integer doInBackground(Void... params) { + synchronized(mStartLock) { + if (isCancelled()) { + return null; + } + + try { + mService.setCallback(getCallerIdentity(), mCallback); + String[] defaultLanguage = mService.getClientDefaultLanguage(); + + mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]); + mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]); + mParams.putString(Engine.KEY_PARAM_VARIANT, defaultLanguage[2]); + + Log.i(TAG, "Set up connection to " + mName); + return SUCCESS; + } catch (RemoteException re) { + Log.e(TAG, "Error connecting to service, setCallback() failed"); + return ERROR; + } + } + } + + @Override + protected void onPostExecute(Integer result) { + synchronized(mStartLock) { + if (mOnSetupConnectionAsyncTask == this) { + mOnSetupConnectionAsyncTask = null; + } + mEstablished = true; + dispatchOnInit(result); + } + } + } + @Override public void onServiceConnected(ComponentName name, IBinder service) { - Log.i(TAG, "Connected to " + name); synchronized(mStartLock) { - if (mServiceConnection != null) { - // Disconnect any previous service connection - mServiceConnection.disconnect(); + mConnectingServiceConnection = null; + + Log.i(TAG, "Connected to " + name); + + if (mOnSetupConnectionAsyncTask != null) { + mOnSetupConnectionAsyncTask.cancel(false); } - mServiceConnection = this; + mService = ITextToSpeechService.Stub.asInterface(service); - try { - mService.setCallback(getCallerIdentity(), mCallback); - dispatchOnInit(SUCCESS); - } catch (RemoteException re) { - Log.e(TAG, "Error connecting to service, setCallback() failed"); - dispatchOnInit(ERROR); - } + mServiceConnection = Connection.this; + + mEstablished = false; + mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name); + mOnSetupConnectionAsyncTask.execute(); } } @@ -1306,37 +1495,63 @@ public class TextToSpeech { return mCallback; } - @Override - public void onServiceDisconnected(ComponentName name) { + /** + * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set. + * + * @return true if we cancel mOnSetupConnectionAsyncTask in progress. + */ + private boolean clearServiceConnection() { synchronized(mStartLock) { + boolean result = false; + if (mOnSetupConnectionAsyncTask != null) { + result = mOnSetupConnectionAsyncTask.cancel(false); + mOnSetupConnectionAsyncTask = null; + } + mService = null; // If this is the active connection, clear it if (mServiceConnection == this) { mServiceConnection = null; } + return result; + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Log.i(TAG, "Asked to disconnect from " + name); + if (clearServiceConnection()) { + /* We need to protect against a rare case where engine + * dies just after successful connection - and we process onServiceDisconnected + * before OnServiceConnectedAsyncTask.onPostExecute. onServiceDisconnected cancels + * OnServiceConnectedAsyncTask.onPostExecute and we don't call dispatchOnInit + * with ERROR as argument. + */ + dispatchOnInit(ERROR); } } public void disconnect() { mContext.unbindService(this); + clearServiceConnection(); + } - synchronized (mStartLock) { - mService = null; - // If this is the active connection, clear it - if (mServiceConnection == this) { - mServiceConnection = null; - } - - } + public boolean isEstablished() { + return mService != null && mEstablished; } - public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) { + public <R> R runAction(Action<R> action, R errorResult, String method, + boolean reconnect, boolean onlyEstablishedConnection) { synchronized (mStartLock) { try { if (mService == null) { Log.w(TAG, method + " failed: not connected to TTS engine"); return errorResult; } + if (onlyEstablishedConnection && !isEstablished()) { + Log.w(TAG, method + " failed: TTS engine connection not fully set up"); + return errorResult; + } return action.run(mService); } catch (RemoteException ex) { Log.e(TAG, method + " failed", ex); @@ -1394,4 +1609,13 @@ public class TextToSpeech { } + /** + * Limit of length of input string passed to speak and synthesizeToFile. + * + * @see #speak + * @see #synthesizeToFile + */ + public static int getMaxSpeechInputLength() { + return 4000; + } } diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index d124e68..1bcf3e0 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -26,6 +26,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; +import android.os.ParcelFileDescriptor; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.Settings; @@ -33,8 +34,8 @@ import android.speech.tts.TextToSpeech.Engine; import android.text.TextUtils; import android.util.Log; -import java.io.File; -import java.io.IOException; +import java.io.FileDescriptor; +import java.io.FileOutputStream; import java.util.HashMap; import java.util.Locale; import java.util.Set; @@ -74,7 +75,7 @@ public abstract class TextToSpeechService extends Service { private static final boolean DBG = false; private static final String TAG = "TextToSpeechService"; - private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000; + private static final String SYNTH_THREAD_NAME = "SynthThread"; private SynthHandler mSynthHandler; @@ -129,6 +130,8 @@ public abstract class TextToSpeechService extends Service { * * Can be called on multiple threads. * + * Its return values HAVE to be consistent with onLoadLanguage. + * * @param lang ISO-3 language code. * @param country ISO-3 country code. May be empty or null. * @param variant Language variant. May be empty or null. @@ -163,6 +166,8 @@ public abstract class TextToSpeechService extends Service { * at some point in the future. * * Can be called on multiple threads. + * In <= Android 4.2 (<= API 17) can be called on main and service binder threads. + * In > Android 4.2 (> API 17) can be called on main and synthesis threads. * * @param lang ISO-3 language code. * @param country ISO-3 country code. May be empty or null. @@ -256,7 +261,6 @@ public abstract class TextToSpeechService extends Service { } private class SynthHandler extends Handler { - private SpeechItem mCurrentSpeechItem = null; public SynthHandler(Looper looper) { @@ -275,7 +279,7 @@ public abstract class TextToSpeechService extends Service { private synchronized SpeechItem maybeRemoveCurrentSpeechItem(Object callerIdentity) { if (mCurrentSpeechItem != null && - mCurrentSpeechItem.getCallerIdentity() == callerIdentity) { + (mCurrentSpeechItem.getCallerIdentity() == callerIdentity)) { SpeechItem current = mCurrentSpeechItem; mCurrentSpeechItem = null; return current; @@ -296,7 +300,6 @@ public abstract class TextToSpeechService extends Service { if (current != null) { current.stop(); } - // The AudioPlaybackHandler will be destroyed by the caller. } @@ -306,8 +309,15 @@ public abstract class TextToSpeechService extends Service { * Called on a service binder thread. */ public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) { + UtteranceProgressDispatcher utterenceProgress = null; + if (speechItem instanceof UtteranceProgressDispatcher) { + utterenceProgress = (UtteranceProgressDispatcher) speechItem; + } + if (!speechItem.isValid()) { - speechItem.dispatchOnError(); + if (utterenceProgress != null) { + utterenceProgress.dispatchOnError(); + } return TextToSpeech.ERROR; } @@ -325,6 +335,7 @@ public abstract class TextToSpeechService extends Service { } }; Message msg = Message.obtain(this, runnable); + // The obj is used to remove all callbacks from the given app in // stopForApp(String). // @@ -334,7 +345,9 @@ public abstract class TextToSpeechService extends Service { return TextToSpeech.SUCCESS; } else { Log.w(TAG, "SynthThread has quit"); - speechItem.dispatchOnError(); + if (utterenceProgress != null) { + utterenceProgress.dispatchOnError(); + } return TextToSpeech.ERROR; } } @@ -370,7 +383,7 @@ public abstract class TextToSpeechService extends Service { } public int stopAll() { - // Stop the current speech item unconditionally. + // Stop the current speech item unconditionally . SpeechItem current = setCurrentSpeechItem(null); if (current != null) { current.stop(); @@ -393,7 +406,7 @@ public abstract class TextToSpeechService extends Service { /** * An item in the synth thread queue. */ - private abstract class SpeechItem implements UtteranceProgressDispatcher { + private abstract class SpeechItem { private final Object mCallerIdentity; protected final Bundle mParams; private final int mCallerUid; @@ -412,6 +425,15 @@ public abstract class TextToSpeechService extends Service { return mCallerIdentity; } + + public int getCallerUid() { + return mCallerUid; + } + + public int getCallerPid() { + return mCallerPid; + } + /** * Checker whether the item is valid. If this method returns false, the item should not * be played. @@ -436,6 +458,8 @@ public abstract class TextToSpeechService extends Service { return playImpl(); } + protected abstract int playImpl(); + /** * Stops the speech item. * Must not be called more than once. @@ -452,6 +476,23 @@ public abstract class TextToSpeechService extends Service { stopImpl(); } + protected abstract void stopImpl(); + + protected synchronized boolean isStopped() { + return mStopped; + } + } + + /** + * An item in the synth thread queue that process utterance. + */ + private abstract class UtteranceSpeechItem extends SpeechItem + implements UtteranceProgressDispatcher { + + public UtteranceSpeechItem(Object caller, int callerUid, int callerPid, Bundle params) { + super(caller, callerUid, callerPid, params); + } + @Override public void dispatchOnDone() { final String utteranceId = getUtteranceId(); @@ -476,22 +517,6 @@ public abstract class TextToSpeechService extends Service { } } - public int getCallerUid() { - return mCallerUid; - } - - public int getCallerPid() { - return mCallerPid; - } - - protected synchronized boolean isStopped() { - return mStopped; - } - - protected abstract int playImpl(); - - protected abstract void stopImpl(); - public int getStreamType() { return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM); } @@ -519,9 +544,10 @@ public abstract class TextToSpeechService extends Service { protected float getFloatParam(String key, float defaultValue) { return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue); } + } - class SynthesisSpeechItem extends SpeechItem { + class SynthesisSpeechItem extends UtteranceSpeechItem { // Never null. private final String mText; private final SynthesisRequest mSynthesisRequest; @@ -552,7 +578,7 @@ public abstract class TextToSpeechService extends Service { Log.e(TAG, "null synthesis text"); return false; } - if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) { + if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) { Log.w(TAG, "Text too long: " + mText.length() + " chars"); return false; } @@ -630,19 +656,19 @@ public abstract class TextToSpeechService extends Service { } } - private class SynthesisToFileSpeechItem extends SynthesisSpeechItem { - private final File mFile; + private class SynthesisToFileSpeechDescriptorItem extends SynthesisSpeechItem { + private final FileDescriptor mFileDescriptor; - public SynthesisToFileSpeechItem(Object callerIdentity, int callerUid, int callerPid, - Bundle params, String text, - File file) { + public SynthesisToFileSpeechDescriptorItem(Object callerIdentity, int callerUid, + int callerPid, Bundle params, String text, FileDescriptor fileDescriptor) { super(callerIdentity, callerUid, callerPid, params, text); - mFile = file; + mFileDescriptor = fileDescriptor; } @Override protected AbstractSynthesisCallback createSynthesisCallback() { - return new FileSynthesisCallback(mFile); + FileOutputStream fileOutputStream = new FileOutputStream(mFileDescriptor); + return new FileSynthesisCallback(fileOutputStream.getChannel()); } @Override @@ -658,7 +684,7 @@ public abstract class TextToSpeechService extends Service { } } - private class AudioSpeechItem extends SpeechItem { + private class AudioSpeechItem extends UtteranceSpeechItem { private final AudioPlaybackQueueItem mItem; public AudioSpeechItem(Object callerIdentity, int callerUid, int callerPid, Bundle params, Uri uri) { @@ -684,7 +710,7 @@ public abstract class TextToSpeechService extends Service { } } - private class SilenceSpeechItem extends SpeechItem { + private class SilenceSpeechItem extends UtteranceSpeechItem { private final long mDuration; public SilenceSpeechItem(Object callerIdentity, int callerUid, int callerPid, @@ -711,6 +737,41 @@ public abstract class TextToSpeechService extends Service { } } + private class LoadLanguageItem extends SpeechItem { + private final String mLanguage; + private final String mCountry; + private final String mVariant; + + public LoadLanguageItem(Object callerIdentity, int callerUid, int callerPid, + Bundle params, String language, String country, String variant) { + super(callerIdentity, callerUid, callerPid, params); + mLanguage = language; + mCountry = country; + mVariant = variant; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + protected int playImpl() { + int result = TextToSpeechService.this.onLoadLanguage(mLanguage, mCountry, mVariant); + if (result == TextToSpeech.LANG_AVAILABLE || + result == TextToSpeech.LANG_COUNTRY_AVAILABLE || + result == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { + return TextToSpeech.SUCCESS; + } + return TextToSpeech.ERROR; + } + + @Override + protected void stopImpl() { + // No-op + } + } + @Override public IBinder onBind(Intent intent) { if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) { @@ -738,15 +799,15 @@ public abstract class TextToSpeechService extends Service { } @Override - public int synthesizeToFile(IBinder caller, String text, String filename, - Bundle params) { - if (!checkNonNull(caller, text, filename, params)) { + public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor + fileDescriptor, Bundle params) { + if (!checkNonNull(caller, text, fileDescriptor, params)) { return TextToSpeech.ERROR; } - File file = new File(filename); - SpeechItem item = new SynthesisToFileSpeechItem(caller, Binder.getCallingUid(), - Binder.getCallingPid(), params, text, file); + SpeechItem item = new SynthesisToFileSpeechDescriptorItem(caller, Binder.getCallingUid(), + Binder.getCallingPid(), params, text, + fileDescriptor.getFileDescriptor()); return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item); } @@ -791,6 +852,11 @@ public abstract class TextToSpeechService extends Service { return onGetLanguage(); } + @Override + public String[] getClientDefaultLanguage() { + return getSettingsLocale(); + } + /* * If defaults are enforced, then no language is "available" except * perhaps the default language selected by the user. @@ -822,12 +888,25 @@ public abstract class TextToSpeechService extends Service { * are enforced. */ @Override - public int loadLanguage(String lang, String country, String variant) { + public int loadLanguage(IBinder caller, String lang, String country, String variant) { if (!checkNonNull(lang)) { return TextToSpeech.ERROR; } + int retVal = onIsLanguageAvailable(lang, country, variant); + + if (retVal == TextToSpeech.LANG_AVAILABLE || + retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE || + retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { - return onLoadLanguage(lang, country, variant); + SpeechItem item = new LoadLanguageItem(caller, Binder.getCallingUid(), + Binder.getCallingPid(), null, lang, country, variant); + + if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) != + TextToSpeech.SUCCESS) { + return TextToSpeech.ERROR; + } + } + return retVal; } @Override diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java index 0c8cbe6..0635559 100644 --- a/core/java/android/test/AndroidTestCase.java +++ b/core/java/android/test/AndroidTestCase.java @@ -20,9 +20,11 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.net.Uri; + import junit.framework.TestCase; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; /** * Extend this if you need to access Resources or other things that depend on Activity Context. @@ -152,11 +154,11 @@ public class AndroidTestCase extends TestCase { * @throws IllegalAccessException */ protected void scrubClass(final Class<?> testCaseClass) - throws IllegalAccessException { + throws IllegalAccessException { final Field[] fields = getClass().getDeclaredFields(); for (Field field : fields) { - final Class<?> fieldClass = field.getDeclaringClass(); - if (testCaseClass.isAssignableFrom(fieldClass) && !field.getType().isPrimitive()) { + if (!field.getType().isPrimitive() && + !Modifier.isStatic(field.getModifiers())) { try { field.setAccessible(true); field.set(this, null); @@ -170,6 +172,4 @@ public class AndroidTestCase extends TestCase { } } } - - } diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index d909362..122f8a1 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -503,8 +503,15 @@ public class DynamicLayout extends Layout mNumberOfBlocks = newNumberOfBlocks; final int deltaLines = newLineCount - (endLine - startLine + 1); - for (int i = firstBlock + numAddedBlocks; i < mNumberOfBlocks; i++) { - mBlockEndLines[i] += deltaLines; + if (deltaLines != 0) { + // Display list whose index is >= mIndexFirstChangedBlock is valid + // but it needs to update its drawing location. + mIndexFirstChangedBlock = firstBlock + numAddedBlocks; + for (int i = mIndexFirstChangedBlock; i < mNumberOfBlocks; i++) { + mBlockEndLines[i] += deltaLines; + } + } else { + mIndexFirstChangedBlock = mNumberOfBlocks; } int blockIndex = firstBlock; @@ -559,6 +566,20 @@ public class DynamicLayout extends Layout return mNumberOfBlocks; } + /** + * @hide + */ + public int getIndexFirstChangedBlock() { + return mIndexFirstChangedBlock; + } + + /** + * @hide + */ + public void setIndexFirstChangedBlock(int i) { + mIndexFirstChangedBlock = i; + } + @Override public int getLineCount() { return mInts.size() - 1; @@ -697,6 +718,8 @@ public class DynamicLayout extends Layout private int[] mBlockIndices; // Number of items actually currently being used in the above 2 arrays private int mNumberOfBlocks; + // The first index of the blocks whose locations are changed + private int mIndexFirstChangedBlock; private int mTopPadding, mBottomPadding; diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java index 831ccc5..60545e5 100644 --- a/core/java/android/text/GraphicsOperations.java +++ b/core/java/android/text/GraphicsOperations.java @@ -38,7 +38,7 @@ extends CharSequence * {@hide} */ void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd, - float x, float y, int flags, Paint p); + float x, float y, Paint p); /** * Just like {@link Paint#measureText}. @@ -55,19 +55,12 @@ extends CharSequence * @hide */ float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex, Paint paint); - - /** - * Just like {@link Paint#getTextRunAdvances}. - * @hide - */ - float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex, Paint paint, int reserved); + float[] advances, int advancesIndex, Paint paint); /** * Just like {@link Paint#getTextRunCursor}. * @hide */ - int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset, + int getTextRunCursor(int contextStart, int contextEnd, int offset, int cursorOpt, Paint p); } diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 1aab911..160c630 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -16,6 +16,7 @@ package android.text; +import android.graphics.Color; import com.android.internal.util.ArrayUtils; import org.ccil.cowan.tagsoup.HTMLSchema; import org.ccil.cowan.tagsoup.Parser; @@ -168,7 +169,7 @@ public class Html { for(int j = 0; j < style.length; j++) { if (style[j] instanceof AlignmentSpan) { - Layout.Alignment align = + Layout.Alignment align = ((AlignmentSpan) style[j]).getAlignment(); needDiv = true; if (align == Layout.Alignment.ALIGN_CENTER) { @@ -181,7 +182,7 @@ public class Html { } } if (needDiv) { - out.append("<div " + elements + ">"); + out.append("<div ").append(elements).append(">"); } withinDiv(out, text, i, next); @@ -199,13 +200,13 @@ public class Html { next = text.nextSpanTransition(i, end, QuoteSpan.class); QuoteSpan[] quotes = text.getSpans(i, next, QuoteSpan.class); - for (QuoteSpan quote: quotes) { + for (QuoteSpan quote : quotes) { out.append("<blockquote>"); } withinBlockquote(out, text, i, next); - for (QuoteSpan quote: quotes) { + for (QuoteSpan quote : quotes) { out.append("</blockquote>\n"); } } @@ -391,7 +392,7 @@ public class Html { } else if (c == '&') { out.append("&"); } else if (c > 0x7E || c < ' ') { - out.append("&#" + ((int) c) + ";"); + out.append("&#").append((int) c).append(";"); } else if (c == ' ') { while (i + 1 < end && text.charAt(i + 1) == ' ') { out.append(" "); @@ -616,8 +617,6 @@ class HtmlToSpannedConverter implements ContentHandler { if (where != len) { text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } - - return; } private static void startImg(SpannableStringBuilder text, @@ -673,7 +672,7 @@ class HtmlToSpannedConverter implements ContentHandler { Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } else { - int c = getHtmlColor(f.mColor); + int c = Color.getHtmlColor(f.mColor); if (c != -1) { text.setSpan(new ForegroundColorSpan(c | 0xFF000000), where, len, @@ -842,47 +841,4 @@ class HtmlToSpannedConverter implements ContentHandler { mLevel = level; } } - - private static HashMap<String,Integer> COLORS = buildColorMap(); - - private static HashMap<String,Integer> buildColorMap() { - HashMap<String,Integer> map = new HashMap<String,Integer>(); - map.put("aqua", 0x00FFFF); - map.put("black", 0x000000); - map.put("blue", 0x0000FF); - map.put("fuchsia", 0xFF00FF); - map.put("green", 0x008000); - map.put("grey", 0x808080); - map.put("lime", 0x00FF00); - map.put("maroon", 0x800000); - map.put("navy", 0x000080); - map.put("olive", 0x808000); - map.put("purple", 0x800080); - map.put("red", 0xFF0000); - map.put("silver", 0xC0C0C0); - map.put("teal", 0x008080); - map.put("white", 0xFFFFFF); - map.put("yellow", 0xFFFF00); - return map; - } - - /** - * Converts an HTML color (named or numeric) to an integer RGB value. - * - * @param color Non-null color string. - * @return A color value, or {@code -1} if the color string could not be interpreted. - */ - private static int getHtmlColor(String color) { - Integer i = COLORS.get(color.toLowerCase()); - if (i != null) { - return i; - } else { - try { - return XmlUtils.convertValueToInt(color, -1); - } catch (NumberFormatException nfe) { - return -1; - } - } - } - } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 123acca..a6e8c70 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -792,8 +792,17 @@ public abstract class Layout { * the paragraph's primary direction. */ public float getPrimaryHorizontal(int offset) { + return getPrimaryHorizontal(offset, false /* not clamped */); + } + + /** + * Get the primary horizontal position for the specified text offset, but + * optionally clamp it so that it doesn't exceed the width of the layout. + * @hide + */ + public float getPrimaryHorizontal(int offset, boolean clamped) { boolean trailing = primaryIsTrailingPrevious(offset); - return getHorizontal(offset, trailing); + return getHorizontal(offset, trailing, clamped); } /** @@ -802,17 +811,26 @@ public abstract class Layout { * the direction other than the paragraph's primary direction. */ public float getSecondaryHorizontal(int offset) { + return getSecondaryHorizontal(offset, false /* not clamped */); + } + + /** + * Get the secondary horizontal position for the specified text offset, but + * optionally clamp it so that it doesn't exceed the width of the layout. + * @hide + */ + public float getSecondaryHorizontal(int offset, boolean clamped) { boolean trailing = primaryIsTrailingPrevious(offset); - return getHorizontal(offset, !trailing); + return getHorizontal(offset, !trailing, clamped); } - private float getHorizontal(int offset, boolean trailing) { + private float getHorizontal(int offset, boolean trailing, boolean clamped) { int line = getLineForOffset(offset); - return getHorizontal(offset, trailing, line); + return getHorizontal(offset, trailing, line, clamped); } - private float getHorizontal(int offset, boolean trailing, int line) { + private float getHorizontal(int offset, boolean trailing, int line, boolean clamped) { int start = getLineStart(line); int end = getLineEnd(line); int dir = getParagraphDirection(line); @@ -834,6 +852,9 @@ public abstract class Layout { float wid = tl.measure(offset - start, trailing, null); TextLine.recycle(tl); + if (clamped && wid > mWidth) { + wid = mWidth; + } int left = getParagraphLeft(line); int right = getParagraphRight(line); @@ -1257,6 +1278,23 @@ public abstract class Layout { } /** + * Determine whether we should clamp cursor position. Currently it's + * only robust for left-aligned displays. + * @hide + */ + public boolean shouldClampCursor(int line) { + // Only clamp cursor position in left-aligned displays. + switch (getParagraphAlignment(line)) { + case ALIGN_LEFT: + return true; + case ALIGN_NORMAL: + return getParagraphDirection(line) > 0; + default: + return false; + } + + } + /** * Fills in the specified Path with a representation of a cursor * at the specified offset. This will often be a vertical line * but can be multiple discontinuous lines in text with multiple @@ -1270,8 +1308,9 @@ public abstract class Layout { int top = getLineTop(line); int bottom = getLineTop(line+1); - float h1 = getPrimaryHorizontal(point) - 0.5f; - float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1; + boolean clamped = shouldClampCursor(line); + float h1 = getPrimaryHorizontal(point, clamped) - 0.5f; + float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1; int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); @@ -1357,8 +1396,8 @@ public abstract class Layout { int en = Math.min(end, there); if (st != en) { - float h1 = getHorizontal(st, false, line); - float h2 = getHorizontal(en, true, line); + float h1 = getHorizontal(st, false, line, false /* not clamped */); + float h2 = getHorizontal(en, true, line, false /* not clamped */); float left = Math.min(h1, h2); float right = Math.max(h1, h2); diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index bd9310c..0c881a4 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -159,18 +159,15 @@ class MeasuredText { mPos = p + len; if (mEasy) { - int flags = mDir == Layout.DIR_LEFT_TO_RIGHT - ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL; - return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p); + return paint.getTextRunAdvances(mChars, p, len, p, len, mWidths, p); } float totalAdvance = 0; int level = mLevels[p]; for (int q = p, i = p + 1, e = p + len;; ++i) { if (i == e || mLevels[i] != level) { - int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL; totalAdvance += - paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q); + paint.getTextRunAdvances(mChars, q, i - q, q, i - q, mWidths, q); if (i == e) { break; } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 0f30d25..9e43671 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1130,20 +1130,20 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * {@hide} */ public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd, - float x, float y, int flags, Paint p) { + float x, float y, Paint p) { checkRange("drawTextRun", start, end); int contextLen = contextEnd - contextStart; int len = end - start; if (contextEnd <= mGapStart) { - c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, flags, p); + c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, p); } else if (contextStart >= mGapStart) { c.drawTextRun(mText, start + mGapLength, len, contextStart + mGapLength, - contextLen, x, y, flags, p); + contextLen, x, y, p); } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); - c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, flags, p); + c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, p); TextUtils.recycle(buf); } } @@ -1200,7 +1200,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * Don't call this yourself -- exists for Paint to use internally. * {@hide} */ - public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags, + public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, float[] advances, int advancesPos, Paint p) { float ret; @@ -1210,44 +1210,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (end <= mGapStart) { ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen, - flags, advances, advancesPos); + advances, advancesPos); } else if (start >= mGapStart) { ret = p.getTextRunAdvances(mText, start + mGapLength, len, - contextStart + mGapLength, contextLen, flags, advances, advancesPos); + contextStart + mGapLength, contextLen, advances, advancesPos); } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); ret = p.getTextRunAdvances(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesPos); - TextUtils.recycle(buf); - } - - return ret; - } - - /** - * Don't call this yourself -- exists for Paint to use internally. - * {@hide} - */ - public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags, - float[] advances, int advancesPos, Paint p, int reserved) { - - float ret; - - int contextLen = contextEnd - contextStart; - int len = end - start; - - if (end <= mGapStart) { - ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen, - flags, advances, advancesPos, reserved); - } else if (start >= mGapStart) { - ret = p.getTextRunAdvances(mText, start + mGapLength, len, - contextStart + mGapLength, contextLen, flags, advances, advancesPos, reserved); - } else { - char[] buf = TextUtils.obtain(contextLen); - getChars(contextStart, contextEnd, buf, 0); - ret = p.getTextRunAdvances(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesPos, reserved); + 0, contextLen, advances, advancesPos); TextUtils.recycle(buf); } @@ -1270,7 +1241,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * * @param contextStart the start index of the context * @param contextEnd the (non-inclusive) end index of the context - * @param flags either DIRECTION_RTL or DIRECTION_LTR + * @param flags reserved * @param offset the cursor position to move from * @param cursorOpt how to move the cursor, one of CURSOR_AFTER, * CURSOR_AT_OR_AFTER, CURSOR_BEFORE, @@ -1281,22 +1252,30 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable */ @Deprecated public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset, - int cursorOpt, Paint p) { + int cursorOpt, Paint p) { + return getTextRunCursor(contextStart, contextEnd, offset, cursorOpt, p); + } + + /** + * @hide + */ + public int getTextRunCursor(int contextStart, int contextEnd, int offset, + int cursorOpt, Paint p) { int ret; int contextLen = contextEnd - contextStart; if (contextEnd <= mGapStart) { ret = p.getTextRunCursor(mText, contextStart, contextLen, - flags, offset, cursorOpt); + offset, cursorOpt); } else if (contextStart >= mGapStart) { ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen, - flags, offset + mGapLength, cursorOpt) - mGapLength; + offset + mGapLength, cursorOpt) - mGapLength; } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); ret = p.getTextRunCursor(buf, 0, contextLen, - flags, offset - contextStart, cursorOpt) + contextStart; + offset - contextStart, cursorOpt) + contextStart; TextUtils.recycle(buf); } diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java index 513e11c..8a4ba42 100644 --- a/core/java/android/text/TextDirectionHeuristic.java +++ b/core/java/android/text/TextDirectionHeuristic.java @@ -17,10 +17,30 @@ package android.text; /** - * Interface for objects that guess at the paragraph direction by examining text. - * - * @hide + * Interface for objects that use a heuristic for guessing at the paragraph direction by examining text. */ public interface TextDirectionHeuristic { - boolean isRtl(char[] text, int start, int count); + /** + * Guess if a chars array is in the RTL direction or not. + * + * @param array the char array. + * @param start start index, inclusive. + * @param count the length to check, must not be negative and not greater than + * {@code array.length - start}. + * @return true if all chars in the range are to be considered in a RTL direction, + * false otherwise. + */ + boolean isRtl(char[] array, int start, int count); + + /** + * Guess if a {@code CharSequence} is in the RTL direction or not. + * + * @param cs the CharSequence. + * @param start start index, inclusive. + * @param count the length to check, must not be negative and not greater than + * {@code CharSequence.length() - start}. + * @return true if all chars in the range are to be considered in a RTL direction, + * false otherwise. + */ + boolean isRtl(CharSequence cs, int start, int count); } diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index df8c4c6..7d7e3a9 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -19,43 +19,45 @@ package android.text; import android.view.View; +import java.nio.CharBuffer; + /** * Some objects that implement TextDirectionHeuristic. * - * @hide */ public class TextDirectionHeuristics { - /** Always decides that the direction is left to right. */ + /** + * Always decides that the direction is left to right. + */ public static final TextDirectionHeuristic LTR = new TextDirectionHeuristicInternal(null /* no algorithm */, false); - /** Always decides that the direction is right to left. */ + /** + * Always decides that the direction is right to left. + */ public static final TextDirectionHeuristic RTL = new TextDirectionHeuristicInternal(null /* no algorithm */, true); /** - * Determines the direction based on the first strong directional character, - * including bidi format chars, falling back to left to right if it - * finds none. This is the default behavior of the Unicode Bidirectional - * Algorithm. + * Determines the direction based on the first strong directional character, including bidi + * format chars, falling back to left to right if it finds none. This is the default behavior + * of the Unicode Bidirectional Algorithm. */ public static final TextDirectionHeuristic FIRSTSTRONG_LTR = new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false); /** - * Determines the direction based on the first strong directional character, - * including bidi format chars, falling back to right to left if it - * finds none. This is similar to the default behavior of the Unicode - * Bidirectional Algorithm, just with different fallback behavior. + * Determines the direction based on the first strong directional character, including bidi + * format chars, falling back to right to left if it finds none. This is similar to the default + * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior. */ public static final TextDirectionHeuristic FIRSTSTRONG_RTL = new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true); /** - * If the text contains any strong right to left non-format character, determines - * that the direction is right to left, falling back to left to right if it - * finds none. + * If the text contains any strong right to left non-format character, determines that the + * direction is right to left, falling back to left to right if it finds none. */ public static final TextDirectionHeuristic ANYRTL_LTR = new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false); @@ -65,8 +67,39 @@ public class TextDirectionHeuristics { */ public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE; - private static enum TriState { - TRUE, FALSE, UNKNOWN; + /** + * State constants for taking care about true / false / unknown + */ + private static final int STATE_TRUE = 0; + private static final int STATE_FALSE = 1; + private static final int STATE_UNKNOWN = 2; + + private static int isRtlText(int directionality) { + switch (directionality) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + return STATE_FALSE; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + return STATE_TRUE; + default: + return STATE_UNKNOWN; + } + } + + private static int isRtlTextOrFormat(int directionality) { + switch (directionality) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + return STATE_FALSE; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + return STATE_TRUE; + default: + return STATE_UNKNOWN; + } } /** @@ -87,21 +120,26 @@ public class TextDirectionHeuristics { abstract protected boolean defaultIsRtl(); @Override - public boolean isRtl(char[] chars, int start, int count) { - if (chars == null || start < 0 || count < 0 || chars.length - count < start) { + public boolean isRtl(char[] array, int start, int count) { + return isRtl(CharBuffer.wrap(array), start, count); + } + + @Override + public boolean isRtl(CharSequence cs, int start, int count) { + if (cs == null || start < 0 || count < 0 || cs.length() - count < start) { throw new IllegalArgumentException(); } if (mAlgorithm == null) { return defaultIsRtl(); } - return doCheck(chars, start, count); + return doCheck(cs, start, count); } - private boolean doCheck(char[] chars, int start, int count) { - switch(mAlgorithm.checkRtl(chars, start, count)) { - case TRUE: + private boolean doCheck(CharSequence cs, int start, int count) { + switch(mAlgorithm.checkRtl(cs, start, count)) { + case STATE_TRUE: return true; - case FALSE: + case STATE_FALSE: return false; default: return defaultIsRtl(); @@ -124,58 +162,26 @@ public class TextDirectionHeuristics { } } - private static TriState isRtlText(int directionality) { - switch (directionality) { - case Character.DIRECTIONALITY_LEFT_TO_RIGHT: - return TriState.FALSE; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - return TriState.TRUE; - default: - return TriState.UNKNOWN; - } - } - - private static TriState isRtlTextOrFormat(int directionality) { - switch (directionality) { - case Character.DIRECTIONALITY_LEFT_TO_RIGHT: - case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: - case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: - return TriState.FALSE; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: - return TriState.TRUE; - default: - return TriState.UNKNOWN; - } - } - /** * Interface for an algorithm to guess the direction of a paragraph of text. - * */ private static interface TextDirectionAlgorithm { /** * Returns whether the range of text is RTL according to the algorithm. - * */ - TriState checkRtl(char[] text, int start, int count); + int checkRtl(CharSequence cs, int start, int count); } /** - * Algorithm that uses the first strong directional character to determine - * the paragraph direction. This is the standard Unicode Bidirectional - * algorithm. - * + * Algorithm that uses the first strong directional character to determine the paragraph + * direction. This is the standard Unicode Bidirectional algorithm. */ private static class FirstStrong implements TextDirectionAlgorithm { @Override - public TriState checkRtl(char[] text, int start, int count) { - TriState result = TriState.UNKNOWN; - for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) { - result = isRtlTextOrFormat(Character.getDirectionality(text[i])); + public int checkRtl(CharSequence cs, int start, int count) { + int result = STATE_UNKNOWN; + for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) { + result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i))); } return result; } @@ -190,25 +196,24 @@ public class TextDirectionHeuristics { * Algorithm that uses the presence of any strong directional non-format * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the * direction of text. - * */ private static class AnyStrong implements TextDirectionAlgorithm { private final boolean mLookForRtl; @Override - public TriState checkRtl(char[] text, int start, int count) { + public int checkRtl(CharSequence cs, int start, int count) { boolean haveUnlookedFor = false; for (int i = start, e = start + count; i < e; ++i) { - switch (isRtlText(Character.getDirectionality(text[i]))) { - case TRUE: + switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) { + case STATE_TRUE: if (mLookForRtl) { - return TriState.TRUE; + return STATE_TRUE; } haveUnlookedFor = true; break; - case FALSE: + case STATE_FALSE: if (!mLookForRtl) { - return TriState.FALSE; + return STATE_FALSE; } haveUnlookedFor = true; break; @@ -217,9 +222,9 @@ public class TextDirectionHeuristics { } } if (haveUnlookedFor) { - return mLookForRtl ? TriState.FALSE : TriState.TRUE; + return mLookForRtl ? STATE_FALSE : STATE_TRUE; } - return TriState.UNKNOWN; + return STATE_UNKNOWN; } private AnyStrong(boolean lookForRtl) { diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 1fecf81..e34a0ef 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -664,14 +664,13 @@ class TextLine { } } - int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; if (mCharsValid) { return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart, - flags, offset, cursorOpt); + offset, cursorOpt); } else { return wp.getTextRunCursor(mText, mStart + spanStart, - mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart; + mStart + spanLimit, mStart + offset, cursorOpt) - mStart; } } @@ -738,15 +737,13 @@ class TextLine { int contextLen = contextEnd - contextStart; if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { - int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; if (mCharsValid) { ret = wp.getTextRunAdvances(mChars, start, runLen, - contextStart, contextLen, flags, null, 0); + contextStart, contextLen, null, 0); } else { int delta = mStart; - ret = wp.getTextRunAdvances(mText, delta + start, - delta + end, delta + contextStart, delta + contextEnd, - flags, null, 0); + ret = wp.getTextRunAdvances(mText, delta + start, delta + end, + delta + contextStart, delta + contextEnd, null, 0); } } @@ -786,8 +783,7 @@ class TextLine { wp.setAntiAlias(previousAntiAlias); } - drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, - x, y + wp.baselineShift); + drawTextRun(c, wp, start, end, contextStart, contextEnd, x, y + wp.baselineShift); } return runIsRtl ? -ret : ret; @@ -970,23 +966,21 @@ class TextLine { * @param end the end of the run * @param contextStart the start of context for the run * @param contextEnd the end of the context for the run - * @param runIsRtl true if the run is right-to-left * @param x the x position of the left edge of the run * @param y the baseline of the run */ private void drawTextRun(Canvas c, TextPaint wp, int start, int end, - int contextStart, int contextEnd, boolean runIsRtl, float x, int y) { + int contextStart, int contextEnd, float x, int y) { - int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR; if (mCharsValid) { int count = end - start; int contextCount = contextEnd - contextStart; c.drawTextRun(mChars, start, count, contextStart, contextCount, - x, y, flags, wp); + x, y, wp); } else { int delta = mStart; c.drawTextRun(mText, delta + start, delta + end, - delta + contextStart, delta + contextEnd, x, y, flags, wp); + delta + contextStart, delta + contextEnd, x, y, wp); } } diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 1508d10..2ab9bf8 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -757,7 +757,7 @@ public class TextUtils { break; case EASY_EDIT_SPAN: - readSpan(p, sp, new EasyEditSpan()); + readSpan(p, sp, new EasyEditSpan(p)); break; case LOCALE_SPAN: diff --git a/core/java/android/text/bidi/BidiFormatter.java b/core/java/android/text/bidi/BidiFormatter.java new file mode 100644 index 0000000..370cbf7 --- /dev/null +++ b/core/java/android/text/bidi/BidiFormatter.java @@ -0,0 +1,1123 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text.bidi; + +import android.text.TextDirectionHeuristic; +import android.text.TextDirectionHeuristics; +import android.text.TextUtils; +import android.view.View; + +import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR; + +import java.util.Locale; + + +/** + * Utility class for formatting text for display in a potentially opposite-directionality context + * without garbling. The directionality of the context is set at formatter creation and the + * directionality of the text can be either estimated or passed in when known. Provides the + * following functionality: + * <p> + * 1. Bidi Wrapping + * When text in one language is mixed into a document in another, opposite-directionality language, + * e.g. when an English business name is embedded in a Hebrew web page, both the inserted string + * and the text surrounding it may be displayed incorrectly unless the inserted string is explicitly + * separated from the surrounding text in a "wrapper" that: + * <p> + * - Declares its directionality so that the string is displayed correctly. This can be done in HTML + * markup (e.g. a 'span dir="rtl"' element) by {@link #spanWrap} and similar methods, or - only in + * contexts where markup can't be used - in Unicode bidi formatting codes by {@link #unicodeWrap} + * and similar methods. + * <p> + * - Isolates the string's directionality, so it does not unduly affect the surrounding content. + * Currently, this can only be done using invisible Unicode characters of the same direction as + * the context (LRM or RLM) in addition to the directionality declaration above, thus "resetting" + * the directionality to that of the context. The "reset" may need to be done at both ends of the + * string. Without "reset" after the string, the string will "stick" to a number or logically + * separate opposite-direction text that happens to follow it in-line (even if separated by + * neutral content like spaces and punctuation). Without "reset" before the string, the same can + * happen there, but only with more opposite-direction text, not a number. One approach is to + * "reset" the direction only after each string, on the theory that if the preceding opposite- + * direction text is itself bidi-wrapped, the "reset" after it will prevent the sticking. (Doing + * the "reset" only before each string definitely does not work because we do not want to require + * bidi-wrapping numbers, and a bidi-wrapped opposite-direction string could be followed by a + * number.) Still, the safest policy is to do the "reset" on both ends of each string, since RTL + * message translations often contain untranslated Latin-script brand names and technical terms, + * and one of these can be followed by a bidi-wrapped inserted value. On the other hand, when one + * has such a message, it is best to do the "reset" manually in the message translation itself, + * since the message's opposite-direction text could be followed by an inserted number, which we + * would not bidi-wrap anyway. Thus, "reset" only after the string is the current default. In an + * alternative to "reset", recent additions to the HTML, CSS, and Unicode standards allow the + * isolation to be part of the directionality declaration. This form of isolation is better than + * "reset" because it takes less space, does not require knowing the context directionality, has a + * gentler effect than "reset", and protects both ends of the string. However, we do not yet allow + * using it because required platforms do not yet support it. + * <p> + * Providing these wrapping services is the basic purpose of the bidi formatter. + * <p> + * 2. Directionality estimation + * How does one know whether a string about to be inserted into surrounding text has the same + * directionality? Well, in many cases, one knows that this must be the case when writing the code + * doing the insertion, e.g. when a localized message is inserted into a localized page. In such + * cases there is no need to involve the bidi formatter at all. In some other cases, it need not be + * the same as the context, but is either constant (e.g. urls are always LTR) or otherwise known. + * In the remaining cases, e.g. when the string is user-entered or comes from a database, the + * language of the string (and thus its directionality) is not known a priori, and must be + * estimated at run-time. The bidi formatter can do this automatically using the default + * first-strong estimation algorithm. It can also be configured to use a custom directionality + * estimation object. + * <p> + * 3. Escaping + * When wrapping plain text - i.e. text that is not already HTML or HTML-escaped - in HTML markup, + * the text must first be HTML-escaped to prevent XSS attacks and other nasty business. This of + * course is always true, but the escaping can not be done after the string has already been wrapped + * in markup, so the bidi formatter also serves as a last chance and includes escaping services. + * <p> + * Thus, in a single call, the formatter will escape the input string as specified, determine its + * directionality, and wrap it as necessary. It is then up to the caller to insert the return value + * in the output. + */ +public final class BidiFormatter { + + /** + * The default text direction heuristic. + */ + private static TextDirectionHeuristic DEFAULT_TEXT_DIRECTION_HEURISTIC = FIRSTSTRONG_LTR; + + /** + * Unicode "Left-To-Right Embedding" (LRE) character. + */ + private static final char LRE = '\u202A'; + + /** + * Unicode "Right-To-Left Embedding" (RLE) character. + */ + private static final char RLE = '\u202B'; + + /** + * Unicode "Pop Directional Formatting" (PDF) character. + */ + private static final char PDF = '\u202C'; + + /** + * Unicode "Left-To-Right Mark" (LRM) character. + */ + private static final char LRM = '\u200E'; + + /* + * Unicode "Right-To-Left Mark" (RLM) character. + */ + private static final char RLM = '\u200F'; + + /* + * String representation of LRM + */ + private static final String LRM_STRING = Character.toString(LRM); + + /* + * String representation of RLM + */ + private static final String RLM_STRING = Character.toString(RLM); + + /** + * "ltr" string constant. + */ + private static final String LTR_STRING = "ltr"; + + /** + * "rtl" string constant. + */ + private static final String RTL_STRING = "rtl"; + + /** + * "dir=\"ltr\"" string constant. + */ + private static final String DIR_LTR_STRING = "dir=\"ltr\""; + + /** + * "dir=\"rtl\"" string constant. + */ + private static final String DIR_RTL_STRING = "dir=\"rtl\""; + + /** + * "right" string constant. + */ + private static final String RIGHT = "right"; + + /** + * "left" string constant. + */ + private static final String LEFT = "left"; + + /** + * Empty string constant. + */ + private static final String EMPTY_STRING = ""; + + /** + * A class for building a BidiFormatter with non-default options. + */ + public static final class Builder { + private boolean isRtlContext; + private int flags; + private TextDirectionHeuristic textDirectionHeuristic; + + /** + * Constructor. + * + */ + public Builder() { + initialize(isRtlLocale(Locale.getDefault())); + } + + /** + * Constructor. + * + * @param rtlContext Whether the context directionality is RTL. + */ + public Builder(boolean rtlContext) { + initialize(rtlContext); + } + + /** + * Constructor. + * + * @param locale The context locale. + */ + public Builder(Locale locale) { + initialize(isRtlLocale(locale)); + } + + /** + * Initializes the builder with the given context directionality and default options. + * + * @param isRtlContext Whether the context is RTL or not. + */ + private void initialize(boolean isRtlContext) { + this.isRtlContext = isRtlContext; + textDirectionHeuristic = DEFAULT_TEXT_DIRECTION_HEURISTIC; + this.flags = DEFAULT_FLAGS; + } + + /** + * Specifies whether the BidiFormatter to be built should also "reset" directionality before + * a string being bidi-wrapped, not just after it. The default is false. + */ + public Builder stereoReset(boolean stereoReset) { + if (stereoReset) { + flags |= FLAG_STEREO_RESET; + } else { + flags &= ~FLAG_STEREO_RESET; + } + return this; + } + + /** + * Specifies the default directionality estimation algorithm to be used by the BidiFormatter. + * By default, uses the first-strong heuristic. + * + * @param heuristic the {@code TextDirectionHeuristic} to use. + * @return the builder itself. + */ + public Builder setTextDirectionHeuristic(TextDirectionHeuristic heuristic) { + this.textDirectionHeuristic = heuristic; + return this; + } + + private static BidiFormatter getDefaultInstanceFromContext(boolean isRtlContext) { + return isRtlContext ? DEFAULT_RTL_INSTANCE : DEFAULT_LTR_INSTANCE; + } + + /** + * @return A BidiFormatter with the specified options. + */ + public BidiFormatter build() { + if (flags == DEFAULT_FLAGS && + textDirectionHeuristic == DEFAULT_TEXT_DIRECTION_HEURISTIC) { + return getDefaultInstanceFromContext(isRtlContext); + } + return new BidiFormatter(isRtlContext, flags, textDirectionHeuristic); + } + } + + // + private static final int FLAG_STEREO_RESET = 2; + private static final int DEFAULT_FLAGS = FLAG_STEREO_RESET; + + private static final BidiFormatter DEFAULT_LTR_INSTANCE = new BidiFormatter( + false /* LTR context */, + DEFAULT_FLAGS, + DEFAULT_TEXT_DIRECTION_HEURISTIC); + + private static final BidiFormatter DEFAULT_RTL_INSTANCE = new BidiFormatter( + true /* RTL context */, + DEFAULT_FLAGS, + DEFAULT_TEXT_DIRECTION_HEURISTIC); + + private final boolean isRtlContext; + private final int flags; + private final TextDirectionHeuristic defaultTextDirectionHeuristic; + + /** + * Factory for creating an instance of BidiFormatter given the context directionality. + * + * @param rtlContext Whether the context directionality is RTL. + */ + public static BidiFormatter getInstance(boolean rtlContext) { + return new Builder(rtlContext).build(); + } + + /** + * Factory for creating an instance of BidiFormatter given the context locale. + * + * @param locale The context locale. + */ + public static BidiFormatter getInstance(Locale locale) { + return new Builder(locale).build(); + } + + /** + * @param isRtlContext Whether the context directionality is RTL or not. + * @param flags The option flags. + * @param heuristic The default text direction heuristic. + */ + private BidiFormatter(boolean isRtlContext, int flags, TextDirectionHeuristic heuristic) { + this.isRtlContext = isRtlContext; + this.flags = flags; + this.defaultTextDirectionHeuristic = heuristic; + } + + /** + * @return Whether the context directionality is RTL + */ + public boolean isRtlContext() { + return isRtlContext; + } + + /** + * @return Whether directionality "reset" should also be done before a string being + * bidi-wrapped, not just after it. + */ + public boolean getStereoReset() { + return (flags & FLAG_STEREO_RESET) != 0; + } + + /** + * Returns "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" if it is LTR. + * + * @param str String whose directionality is to be estimated. + * @return "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" otherwise. + */ + public String dirAttrValue(String str) { + return dirAttrValue(isRtl(str)); + } + + /** + * Operates like {@link #dirAttrValue(String)}, but uses a given heuristic to estimate the + * {@code str}'s directionality. + * + * @param str String whose directionality is to be estimated. + * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s + * directionality. + * @return "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" otherwise. + */ + public String dirAttrValue(String str, TextDirectionHeuristic heuristic) { + return dirAttrValue(heuristic.isRtl(str, 0, str.length())); + } + + /** + * Returns "rtl" if the given directionality is RTL, and "ltr" if it is LTR. + * + * @param isRtl Whether the directionality is RTL or not. + * @return "rtl" if the given directionality is RTL, and "ltr" otherwise. + */ + public String dirAttrValue(boolean isRtl) { + return isRtl ? RTL_STRING : LTR_STRING; + } + + /** + * Returns "dir=\"ltr\"" or "dir=\"rtl\"", depending on {@code str}'s estimated directionality, + * if it is not the same as the context directionality. Otherwise, returns the empty string. + * + * @param str String whose directionality is to be estimated. + * @return "dir=\"rtl\"" for RTL text in non-RTL context; "dir=\"ltr\"" for LTR text in non-LTR + * context; else, the empty string. + */ + public String dirAttr(String str) { + return dirAttr(isRtl(str)); + } + + /** + * Operates like {@link #dirAttr(String)}, but uses a given heuristic to estimate the + * {@code str}'s directionality. + * + * @param str String whose directionality is to be estimated. + * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s + * directionality. + * @return "dir=\"rtl\"" for RTL text in non-RTL context; "dir=\"ltr\"" for LTR text in non-LTR + * context; else, the empty string. + */ + public String dirAttr(String str, TextDirectionHeuristic heuristic) { + return dirAttr(heuristic.isRtl(str, 0, str.length())); + } + + /** + * Returns "dir=\"ltr\"" or "dir=\"rtl\"", depending on the given directionality, if it is not + * the same as the context directionality. Otherwise, returns the empty string. + * + * @param isRtl Whether the directionality is RTL or not + * @return "dir=\"rtl\"" for RTL text in non-RTL context; "dir=\"ltr\"" for LTR text in non-LTR + * context; else, the empty string. + */ + public String dirAttr(boolean isRtl) { + return (isRtl != isRtlContext) ? (isRtl ? DIR_RTL_STRING : DIR_LTR_STRING) : EMPTY_STRING; + } + + /** + * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) if either the + * overall or the exit directionality of a given string is opposite to the context directionality. + * Putting this after the string (including its directionality declaration wrapping) prevents it + * from "sticking" to other opposite-directionality text or a number appearing after it inline + * with only neutral content in between. Otherwise returns the empty string. While the exit + * directionality is determined by scanning the end of the string, the overall directionality is + * given explicitly in {@code dir}. + * + * @param str String after which the mark may need to appear. + * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context; + * else, the empty string. + */ + public String markAfter(String str) { + return markAfter(str, defaultTextDirectionHeuristic); + } + + /** + * Operates like {@link #markAfter(String)}, but uses a given heuristic to estimate the + * {@code str}'s directionality. + * + * @param str String after which the mark may need to appear. + * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s + * directionality. + * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context; + * else, the empty string. + */ + public String markAfter(String str, TextDirectionHeuristic heuristic) { + final boolean isRtl = heuristic.isRtl(str, 0, str.length()); + // getExitDir() is called only if needed (short-circuit). + if (!isRtlContext && (isRtl || getExitDir(str) == DIR_RTL)) { + return LRM_STRING; + } + if (isRtlContext && (!isRtl || getExitDir(str) == DIR_LTR)) { + return RLM_STRING; + } + return EMPTY_STRING; + } + + /** + * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) if either the + * overall or the entry directionality of a given string is opposite to the context + * directionality. Putting this before the string (including its directionality declaration + * wrapping) prevents it from "sticking" to other opposite-directionality text appearing before it + * inline with only neutral content in between. Otherwise returns the empty string. While the + * entry directionality is determined by scanning the beginning of the string, the overall + * directionality is given explicitly in {@code dir}. + * + * @param str String before which the mark may need to appear. + * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context; + * else, the empty string. + */ + public String markBefore(String str) { + return markBefore(str, defaultTextDirectionHeuristic); + } + + /** + * Operates like {@link #markBefore(String)}, but uses a given heuristic to estimate the + * {@code str}'s directionality. + * + * @param str String before which the mark may need to appear. + * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s + * directionality. + * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context; + * else, the empty string. + */ + public String markBefore(String str, TextDirectionHeuristic heuristic) { + final boolean isRtl = heuristic.isRtl(str, 0, str.length()); + // getEntryDir() is called only if needed (short-circuit). + if (!isRtlContext && (isRtl || getEntryDir(str) == DIR_RTL)) { + return LRM_STRING; + } + if (isRtlContext && (!isRtl || getEntryDir(str) == DIR_LTR)) { + return RLM_STRING; + } + return EMPTY_STRING; + } + + /** + * Returns the Unicode bidi mark matching the context directionality (LRM for LTR context + * directionality, RLM for RTL context directionality). + */ + public String mark() { + return isRtlContext ? RLM_STRING : LRM_STRING; + } + + /** + * Returns "right" for RTL context directionality. Otherwise for LTR context directionality + * returns "left". + */ + public String startEdge() { + return isRtlContext ? RIGHT : LEFT; + } + + /** + * Returns "left" for RTL context directionality. Otherwise for LTR context directionality + * returns "right". + */ + public String endEdge() { + return isRtlContext ? LEFT : RIGHT; + } + + /** + * Estimates the directionality of a string using the default text direction heuristic. + * + * @param str String whose directionality is to be estimated. + * @return true if {@code str}'s estimated overall directionality is RTL. Otherwise returns + * false. + */ + public boolean isRtl(String str) { + return defaultTextDirectionHeuristic.isRtl(str, 0, str.length()); + } + + /** + * Formats a given string of unknown directionality for use in HTML output of the context + * directionality, so an opposite-directionality string is neither garbled nor garbles its + * surroundings. + * <p> + * The algorithm: estimates the directionality of the given string using the given heuristic. + * If the directionality is known, pass TextDirectionHeuristics.LTR or RTL for heuristic. + * In case its directionality doesn't match the context directionality, wraps it with a 'span' + * element and adds a "dir" attribute (either 'dir=\"rtl\"' or 'dir=\"ltr\"'). + * <p> + * If {@code isolate}, directionally isolates the string so that it does not garble its + * surroundings. Currently, this is done by "resetting" the directionality after the string by + * appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when + * either the overall directionality or the exit directionality of the string is opposite to that + * of the context. If the formatter was built using {@link Builder#stereoReset(boolean)} and + * passing "true" as an argument, also prepends a Unicode bidi mark matching the context + * directionality when either the overall directionality or the entry directionality of the + * string is opposite to that of the context. + * <p> + * + * @param str The input string. + * @param heuristic The algorithm to be used to estimate the string's overall direction. + * @param isolate Whether to directionally isolate the string to prevent it from garbling the + * content around it. + * @return Input string after applying the above processing. + */ + public String spanWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) { + final boolean isRtl = heuristic.isRtl(str, 0, str.length()); + String origStr = str; + str = TextUtils.htmlEncode(str); + + StringBuilder result = new StringBuilder(); + if (getStereoReset() && isolate) { + result.append(markBefore(origStr, + isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR)); + } + if (isRtl != isRtlContext) { + result.append("<span ").append(dirAttr(isRtl)).append('>').append(str).append("</span>"); + } else { + result.append(str); + } + if (isolate) { + result.append(markAfter(origStr, + isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR)); + } + return result.toString(); + } + + /** + * Operates like {@link #spanWrap(String, TextDirectionHeuristic, boolean)}, but assumes + * {@code isolate} is true. + * + * @param str The input string. + * @param heuristic The algorithm to be used to estimate the string's overall direction. + * @return Input string after applying the above processing. + */ + public String spanWrap(String str, TextDirectionHeuristic heuristic) { + return spanWrap(str, heuristic, true /* isolate */); + } + + /** + * Operates like {@link #spanWrap(String, TextDirectionHeuristic, boolean)}, but uses the + * formatter's default direction estimation algorithm. + * + * @param str The input string. + * @param isolate Whether to directionally isolate the string to prevent it from garbling the + * content around it + * @return Input string after applying the above processing. + */ + public String spanWrap(String str, boolean isolate) { + return spanWrap(str, defaultTextDirectionHeuristic, isolate); + } + + /** + * Operates like {@link #spanWrap(String, TextDirectionHeuristic, boolean)}, but uses the + * formatter's default direction estimation algorithm and assumes {@code isolate} is true. + * + * @param str The input string. + * @return Input string after applying the above processing. + */ + public String spanWrap(String str) { + return spanWrap(str, defaultTextDirectionHeuristic, true /* isolate */); + } + + /** + * Formats a string of given directionality for use in plain-text output of the context + * directionality, so an opposite-directionality string is neither garbled nor garbles its + * surroundings. As opposed to {@link #spanWrap}, this makes use of Unicode bidi + * formatting characters. In HTML, its *only* valid use is inside of elements that do not allow + * markup, e.g. the 'option' and 'title' elements. + * <p> + * The algorithm: In case the given directionality doesn't match the context directionality, wraps + * the string with Unicode bidi formatting characters: RLE+{@code str}+PDF for RTL text, or + * LRE+{@code str}+PDF for LTR text. + * <p> + * If {@code isolate}, directionally isolates the string so that it does not garble its + * surroundings. Currently, this is done by "resetting" the directionality after the string by + * appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when + * either the overall directionality or the exit directionality of the string is opposite to that + * of the context. If the formatter was built using {@link Builder#stereoReset(boolean)} and + * passing "true" as an argument, also prepends a Unicode bidi mark matching the context + * directionality when either the overall directionality or the entry directionality of the + * string is opposite to that of the context. Note that as opposed to the overall + * directionality, the entry and exit directionalities are determined from the string itself. + * <p> + * Does *not* do HTML-escaping. + * + * @param str The input string. + * @param heuristic The algorithm to be used to estimate the string's overall direction. + * @param isolate Whether to directionally isolate the string to prevent it from garbling the + * content around it + * @return Input string after applying the above processing. + */ + public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) { + final boolean isRtl = heuristic.isRtl(str, 0, str.length()); + StringBuilder result = new StringBuilder(); + if (getStereoReset() && isolate) { + result.append(markBefore(str, + isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR)); + } + if (isRtl != isRtlContext) { + result.append(isRtl ? RLE : LRE); + result.append(str); + result.append(PDF); + } else { + result.append(str); + } + if (isolate) { + result.append(markAfter(str, + isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR)); + } + return result.toString(); + } + + /** + * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but assumes + * {@code isolate} is true. + * + * @param str The input string. + * @param heuristic The algorithm to be used to estimate the string's overall direction. + * @return Input string after applying the above processing. + */ + public String unicodeWrap(String str, TextDirectionHeuristic heuristic) { + return unicodeWrap(str, heuristic, true /* isolate */); + } + + /** + * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but uses the + * formatter's default direction estimation algorithm. + * + * @param str The input string. + * @param isolate Whether to directionally isolate the string to prevent it from garbling the + * content around it + * @return Input string after applying the above processing. + */ + public String unicodeWrap(String str, boolean isolate) { + return unicodeWrap(str, defaultTextDirectionHeuristic, isolate); + } + + /** + * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but uses the + * formatter's default direction estimation algorithm and assumes {@code isolate} is true. + * + * @param str The input string. + * @return Input string after applying the above processing. + */ + public String unicodeWrap(String str) { + return unicodeWrap(str, defaultTextDirectionHeuristic, true /* isolate */); + } + + /** + * Helper method to return true if the Locale directionality is RTL. + * + * @param locale The Locale whose directionality will be checked to be RTL or LTR + * @return true if the {@code locale} directionality is RTL. False otherwise. + */ + private static boolean isRtlLocale(Locale locale) { + return (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL); + } + + /** + * Enum for directionality type. + */ + private static final int DIR_LTR = -1; + private static final int DIR_UNKNOWN = 0; + private static final int DIR_RTL = +1; + + /** + * Returns the directionality of the last character with strong directionality in the string, or + * DIR_UNKNOWN if none was encountered. For efficiency, actually scans backwards from the end of + * the string. Treats a non-BN character between an LRE/RLE/LRO/RLO and its matching PDF as a + * strong character, LTR after LRE/LRO, and RTL after RLE/RLO. The results are undefined for a + * string containing unbalanced LRE/RLE/LRO/RLO/PDF characters. The intended use is to check + * whether a logically separate item that starts with a number or a character of the string's + * exit directionality and follows this string inline (not counting any neutral characters in + * between) would "stick" to it in an opposite-directionality context, thus being displayed in + * an incorrect position. An LRM or RLM character (the one of the context's directionality) + * between the two will prevent such sticking. + * + * @param str the string to check. + */ + private static int getExitDir(String str) { + return new DirectionalityEstimator(str, false /* isHtml */).getExitDir(); + } + + /** + * Returns the directionality of the first character with strong directionality in the string, + * or DIR_UNKNOWN if none was encountered. Treats a non-BN character between an + * LRE/RLE/LRO/RLO and its matching PDF as a strong character, LTR after LRE/LRO, and RTL after + * RLE/RLO. The results are undefined for a string containing unbalanced LRE/RLE/LRO/RLO/PDF + * characters. The intended use is to check whether a logically separate item that ends with a + * character of the string's entry directionality and precedes the string inline (not counting + * any neutral characters in between) would "stick" to it in an opposite-directionality context, + * thus being displayed in an incorrect position. An LRM or RLM character (the one of the + * context's directionality) between the two will prevent such sticking. + * + * @param str the string to check. + */ + private static int getEntryDir(String str) { + return new DirectionalityEstimator(str, false /* isHtml */).getEntryDir(); + } + + /** + * An object that estimates the directionality of a given string by various methods. + * + */ + private static class DirectionalityEstimator { + + // Internal static variables and constants. + + /** + * Size of the bidi character class cache. The results of the Character.getDirectionality() + * calls on the lowest DIR_TYPE_CACHE_SIZE codepoints are kept in an array for speed. + * The 0x700 value is designed to leave all the European and Near Eastern languages in the + * cache. It can be reduced to 0x180, restricting the cache to the Western European + * languages. + */ + private static final int DIR_TYPE_CACHE_SIZE = 0x700; + + /** + * The bidi character class cache. + */ + private static final byte DIR_TYPE_CACHE[]; + + static { + DIR_TYPE_CACHE = new byte[DIR_TYPE_CACHE_SIZE]; + for (int i = 0; i < DIR_TYPE_CACHE_SIZE; i++) { + DIR_TYPE_CACHE[i] = Character.getDirectionality(i); + } + } + + // Internal instance variables. + + /** + * The text to be scanned. + */ + private final String text; + + /** + * Whether the text to be scanned is to be treated as HTML, i.e. skipping over tags and + * entities when looking for the next / preceding dir type. + */ + private final boolean isHtml; + + /** + * The length of the text in chars. + */ + private final int length; + + /** + * The current position in the text. + */ + private int charIndex; + + /** + * The char encountered by the last dirTypeForward or dirTypeBackward call. If it + * encountered a supplementary codepoint, this contains a char that is not a valid + * codepoint. This is ok, because this member is only used to detect some well-known ASCII + * syntax, e.g. "http://" and the beginning of an HTML tag or entity. + */ + private char lastChar; + + /** + * Constructor. + * + * @param text The string to scan. + * @param isHtml Whether the text to be scanned is to be treated as HTML, i.e. skipping over + * tags and entities. + */ + DirectionalityEstimator(String text, boolean isHtml) { + this.text = text; + this.isHtml = isHtml; + length = text.length(); + } + + /** + * Returns the directionality of the first character with strong directionality in the + * string, or DIR_UNKNOWN if none was encountered. Treats a non-BN character between an + * LRE/RLE/LRO/RLO and its matching PDF as a strong character, LTR after LRE/LRO, and RTL + * after RLE/RLO. The results are undefined for a string containing unbalanced + * LRE/RLE/LRO/RLO/PDF characters. + */ + int getEntryDir() { + // The reason for this method name, as opposed to getFirstStrongDir(), is that + // "first strong" is a commonly used description of Unicode's estimation algorithm, + // but the two must treat formatting characters quite differently. Thus, we are staying + // away from both "first" and "last" in these method names to avoid confusion. + charIndex = 0; + int embeddingLevel = 0; + int embeddingLevelDir = DIR_UNKNOWN; + int firstNonEmptyEmbeddingLevel = 0; + while (charIndex < length && firstNonEmptyEmbeddingLevel == 0) { + switch (dirTypeForward()) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + ++embeddingLevel; + embeddingLevelDir = DIR_LTR; + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + ++embeddingLevel; + embeddingLevelDir = DIR_RTL; + break; + case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: + --embeddingLevel; + // To restore embeddingLevelDir to its previous value, we would need a + // stack, which we want to avoid. Thus, at this point we do not know the + // current embedding's directionality. + embeddingLevelDir = DIR_UNKNOWN; + break; + case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL: + break; + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + if (embeddingLevel == 0) { + return DIR_LTR; + } + firstNonEmptyEmbeddingLevel = embeddingLevel; + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + if (embeddingLevel == 0) { + return DIR_RTL; + } + firstNonEmptyEmbeddingLevel = embeddingLevel; + break; + default: + firstNonEmptyEmbeddingLevel = embeddingLevel; + break; + } + } + + // We have either found a non-empty embedding or scanned the entire string finding + // neither a non-empty embedding nor a strong character outside of an embedding. + if (firstNonEmptyEmbeddingLevel == 0) { + // We have not found a non-empty embedding. Thus, the string contains neither a + // non-empty embedding nor a strong character outside of an embedding. + return DIR_UNKNOWN; + } + + // We have found a non-empty embedding. + if (embeddingLevelDir != DIR_UNKNOWN) { + // We know the directionality of the non-empty embedding. + return embeddingLevelDir; + } + + // We do not remember the directionality of the non-empty embedding we found. So, we go + // backwards to find the start of the non-empty embedding and get its directionality. + while (charIndex > 0) { + switch (dirTypeBackward()) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + if (firstNonEmptyEmbeddingLevel == embeddingLevel) { + return DIR_LTR; + } + --embeddingLevel; + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + if (firstNonEmptyEmbeddingLevel == embeddingLevel) { + return DIR_RTL; + } + --embeddingLevel; + break; + case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: + ++embeddingLevel; + break; + } + } + // We should never get here. + return DIR_UNKNOWN; + } + + /** + * Returns the directionality of the last character with strong directionality in the + * string, or DIR_UNKNOWN if none was encountered. For efficiency, actually scans backwards + * from the end of the string. Treats a non-BN character between an LRE/RLE/LRO/RLO and its + * matching PDF as a strong character, LTR after LRE/LRO, and RTL after RLE/RLO. The results + * are undefined for a string containing unbalanced LRE/RLE/LRO/RLO/PDF characters. + */ + int getExitDir() { + // The reason for this method name, as opposed to getLastStrongDir(), is that "last + // strong" sounds like the exact opposite of "first strong", which is a commonly used + // description of Unicode's estimation algorithm (getUnicodeDir() above), but the two + // must treat formatting characters quite differently. Thus, we are staying away from + // both "first" and "last" in these method names to avoid confusion. + charIndex = length; + int embeddingLevel = 0; + int lastNonEmptyEmbeddingLevel = 0; + while (charIndex > 0) { + switch (dirTypeBackward()) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + if (embeddingLevel == 0) { + return DIR_LTR; + } + if (lastNonEmptyEmbeddingLevel == 0) { + lastNonEmptyEmbeddingLevel = embeddingLevel; + } + break; + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + if (lastNonEmptyEmbeddingLevel == embeddingLevel) { + return DIR_LTR; + } + --embeddingLevel; + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + if (embeddingLevel == 0) { + return DIR_RTL; + } + if (lastNonEmptyEmbeddingLevel == 0) { + lastNonEmptyEmbeddingLevel = embeddingLevel; + } + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + if (lastNonEmptyEmbeddingLevel == embeddingLevel) { + return DIR_RTL; + } + --embeddingLevel; + break; + case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: + ++embeddingLevel; + break; + case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL: + break; + default: + if (lastNonEmptyEmbeddingLevel == 0) { + lastNonEmptyEmbeddingLevel = embeddingLevel; + } + break; + } + } + return DIR_UNKNOWN; + } + + // Internal methods + + /** + * Gets the bidi character class, i.e. Character.getDirectionality(), of a given char, using + * a cache for speed. Not designed for supplementary codepoints, whose results we do not + * cache. + */ + private static byte getCachedDirectionality(char c) { + return c < DIR_TYPE_CACHE_SIZE ? DIR_TYPE_CACHE[c] : Character.getDirectionality(c); + } + + /** + * Returns the Character.DIRECTIONALITY_... value of the next codepoint and advances + * charIndex. If isHtml, and the codepoint is '<' or '&', advances through the tag/entity, + * and returns Character.DIRECTIONALITY_WHITESPACE. For an entity, it would be best to + * figure out the actual character, and return its dirtype, but treating it as whitespace is + * good enough for our purposes. + * + * @throws java.lang.IndexOutOfBoundsException if called when charIndex >= length or < 0. + */ + byte dirTypeForward() { + lastChar = text.charAt(charIndex); + if (Character.isHighSurrogate(lastChar)) { + int codePoint = Character.codePointAt(text, charIndex); + charIndex += Character.charCount(codePoint); + return Character.getDirectionality(codePoint); + } + charIndex++; + byte dirType = getCachedDirectionality(lastChar); + if (isHtml) { + // Process tags and entities. + if (lastChar == '<') { + dirType = skipTagForward(); + } else if (lastChar == '&') { + dirType = skipEntityForward(); + } + } + return dirType; + } + + /** + * Returns the Character.DIRECTIONALITY_... value of the preceding codepoint and advances + * charIndex backwards. If isHtml, and the codepoint is the end of a complete HTML tag or + * entity, advances over the whole tag/entity and returns + * Character.DIRECTIONALITY_WHITESPACE. For an entity, it would be best to figure out the + * actual character, and return its dirtype, but treating it as whitespace is good enough + * for our purposes. + * + * @throws java.lang.IndexOutOfBoundsException if called when charIndex > length or <= 0. + */ + byte dirTypeBackward() { + lastChar = text.charAt(charIndex - 1); + if (Character.isLowSurrogate(lastChar)) { + int codePoint = Character.codePointBefore(text, charIndex); + charIndex -= Character.charCount(codePoint); + return Character.getDirectionality(codePoint); + } + charIndex--; + byte dirType = getCachedDirectionality(lastChar); + if (isHtml) { + // Process tags and entities. + if (lastChar == '>') { + dirType = skipTagBackward(); + } else if (lastChar == ';') { + dirType = skipEntityBackward(); + } + } + return dirType; + } + + /** + * Advances charIndex forward through an HTML tag (after the opening < has already been + * read) and returns Character.DIRECTIONALITY_WHITESPACE. If there is no matching >, + * does not change charIndex and returns Character.DIRECTIONALITY_OTHER_NEUTRALS (for the + * < that hadn't been part of a tag after all). + */ + private byte skipTagForward() { + int initialCharIndex = charIndex; + while (charIndex < length) { + lastChar = text.charAt(charIndex++); + if (lastChar == '>') { + // The end of the tag. + return Character.DIRECTIONALITY_WHITESPACE; + } + if (lastChar == '"' || lastChar == '\'') { + // Skip over a quoted attribute value inside the tag. + char quote = lastChar; + while (charIndex < length && (lastChar = text.charAt(charIndex++)) != quote) {} + } + } + // The original '<' wasn't the start of a tag after all. + charIndex = initialCharIndex; + lastChar = '<'; + return Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + + /** + * Advances charIndex backward through an HTML tag (after the closing > has already been + * read) and returns Character.DIRECTIONALITY_WHITESPACE. If there is no matching <, does + * not change charIndex and returns Character.DIRECTIONALITY_OTHER_NEUTRALS (for the > + * that hadn't been part of a tag after all). Nevertheless, the running time for calling + * skipTagBackward() in a loop remains linear in the size of the text, even for a text like + * ">>>>", because skipTagBackward() also stops looking for a matching < + * when it encounters another >. + */ + private byte skipTagBackward() { + int initialCharIndex = charIndex; + while (charIndex > 0) { + lastChar = text.charAt(--charIndex); + if (lastChar == '<') { + // The start of the tag. + return Character.DIRECTIONALITY_WHITESPACE; + } + if (lastChar == '>') { + break; + } + if (lastChar == '"' || lastChar == '\'') { + // Skip over a quoted attribute value inside the tag. + char quote = lastChar; + while (charIndex > 0 && (lastChar = text.charAt(--charIndex)) != quote) {} + } + } + // The original '>' wasn't the end of a tag after all. + charIndex = initialCharIndex; + lastChar = '>'; + return Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + + /** + * Advances charIndex forward through an HTML character entity tag (after the opening + * & has already been read) and returns Character.DIRECTIONALITY_WHITESPACE. It would be + * best to figure out the actual character and return its dirtype, but this is good enough. + */ + private byte skipEntityForward() { + while (charIndex < length && (lastChar = text.charAt(charIndex++)) != ';') {} + return Character.DIRECTIONALITY_WHITESPACE; + } + + /** + * Advances charIndex backward through an HTML character entity tag (after the closing ; + * has already been read) and returns Character.DIRECTIONALITY_WHITESPACE. It would be best + * to figure out the actual character and return its dirtype, but this is good enough. + * If there is no matching &, does not change charIndex and returns + * Character.DIRECTIONALITY_OTHER_NEUTRALS (for the ';' that did not start an entity after + * all). Nevertheless, the running time for calling skipEntityBackward() in a loop remains + * linear in the size of the text, even for a text like ";;;;;;;", because skipTagBackward() + * also stops looking for a matching & when it encounters another ;. + */ + private byte skipEntityBackward() { + int initialCharIndex = charIndex; + while (charIndex > 0) { + lastChar = text.charAt(--charIndex); + if (lastChar == '&') { + return Character.DIRECTIONALITY_WHITESPACE; + } + if (lastChar == ';') { + break; + } + } + charIndex = initialCharIndex; + lastChar = ';'; + return Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + } +}
\ No newline at end of file diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index cc676de..7e230ac 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -447,6 +447,16 @@ public class DateFormat { * @hide */ public static boolean hasSeconds(CharSequence inFormat) { + return hasDesignator(inFormat, SECONDS); + } + + /** + * Test if a format string contains the given designator. Always returns + * {@code false} if the input format is {@code null}. + * + * @hide + */ + public static boolean hasDesignator(CharSequence inFormat, char designator) { if (inFormat == null) return false; final int length = inFormat.length(); @@ -460,7 +470,7 @@ public class DateFormat { if (c == QUOTE) { count = skipQuotedText(inFormat, i, length); - } else if (c == SECONDS) { + } else if (c == designator) { return true; } } diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java index 3d9daed..c95df46 100644 --- a/core/java/android/text/method/DigitsKeyListener.java +++ b/core/java/android/text/method/DigitsKeyListener.java @@ -49,13 +49,22 @@ public class DigitsKeyListener extends NumberKeyListener * @see KeyEvent#getMatch * @see #getAcceptedChars */ - private static final char[][] CHARACTERS = new char[][] { - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' }, - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' }, - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.' }, + private static final char[][] CHARACTERS = { + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+', '.' }, }; + private static boolean isSignChar(final char c) { + return c == '-' || c == '+'; + } + + // TODO: Needs internationalization + private static boolean isDecimalPointChar(final char c) { + return c == '.'; + } + /** * Allocates a DigitsKeyListener that accepts the digits 0 through 9. */ @@ -145,32 +154,32 @@ public class DigitsKeyListener extends NumberKeyListener int dlen = dest.length(); /* - * Find out if the existing text has '-' or '.' characters. + * Find out if the existing text has a sign or decimal point characters. */ for (int i = 0; i < dstart; i++) { char c = dest.charAt(i); - if (c == '-') { + if (isSignChar(c)) { sign = i; - } else if (c == '.') { + } else if (isDecimalPointChar(c)) { decimal = i; } } for (int i = dend; i < dlen; i++) { char c = dest.charAt(i); - if (c == '-') { - return ""; // Nothing can be inserted in front of a '-'. - } else if (c == '.') { + if (isSignChar(c)) { + return ""; // Nothing can be inserted in front of a sign character. + } else if (isDecimalPointChar(c)) { decimal = i; } } /* * If it does, we must strip them out from the source. - * In addition, '-' must be the very first character, - * and nothing can be inserted before an existing '-'. + * In addition, a sign character must be the very first character, + * and nothing can be inserted before an existing sign character. * Go in reverse order so the offsets are stable. */ @@ -180,7 +189,7 @@ public class DigitsKeyListener extends NumberKeyListener char c = source.charAt(i); boolean strip = false; - if (c == '-') { + if (isSignChar(c)) { if (i != start || dstart != 0) { strip = true; } else if (sign >= 0) { @@ -188,7 +197,7 @@ public class DigitsKeyListener extends NumberKeyListener } else { sign = i; } - } else if (c == '.') { + } else if (isDecimalPointChar(c)) { if (decimal >= 0) { strip = true; } else { diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java index 2feb719..03b4f60 100644 --- a/core/java/android/text/style/EasyEditSpan.java +++ b/core/java/android/text/style/EasyEditSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.app.PendingIntent; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextUtils; @@ -25,12 +26,62 @@ import android.widget.TextView; * Provides an easy way to edit a portion of text. * <p> * The {@link TextView} uses this span to allow the user to delete a chuck of text in one click. - * the text. {@link TextView} removes this span as soon as the text is edited, or the cursor moves. + * <p> + * {@link TextView} removes the span when the user deletes the whole text or modifies it. + * <p> + * This span can be also used to receive notification when the user deletes or modifies the text; */ public class EasyEditSpan implements ParcelableSpan { + /** + * The extra key field in the pending intent that describes how the text changed. + * + * @see #TEXT_DELETED + * @see #TEXT_MODIFIED + * @see #getPendingIntent() + */ + public static final String EXTRA_TEXT_CHANGED_TYPE = + "android.text.style.EXTRA_TEXT_CHANGED_TYPE"; + + /** + * The value of {@link #EXTRA_TEXT_CHANGED_TYPE} when the text wrapped by this span is deleted. + */ + public static final int TEXT_DELETED = 1; + + /** + * The value of {@link #EXTRA_TEXT_CHANGED_TYPE} when the text wrapped by this span is modified. + */ + public static final int TEXT_MODIFIED = 2; + + private final PendingIntent mPendingIntent; + + private boolean mDeleteEnabled; + + /** + * Creates the span. No intent is sent when the wrapped text is modified or + * deleted. + */ public EasyEditSpan() { - // Empty + mPendingIntent = null; + mDeleteEnabled = true; + } + + /** + * @param pendingIntent The intent will be sent when the wrapped text is deleted or modified. + * When the pending intent is sent, {@link #EXTRA_TEXT_CHANGED_TYPE} is + * added in the intent to describe how the text changed. + */ + public EasyEditSpan(PendingIntent pendingIntent) { + mPendingIntent = pendingIntent; + mDeleteEnabled = true; + } + + /** + * Constructor called from {@link TextUtils} to restore the span. + */ + public EasyEditSpan(Parcel source) { + mPendingIntent = source.readParcelable(null); + mDeleteEnabled = (source.readByte() == 1); } @Override @@ -40,11 +91,39 @@ public class EasyEditSpan implements ParcelableSpan { @Override public void writeToParcel(Parcel dest, int flags) { - // Empty + dest.writeParcelable(mPendingIntent, 0); + dest.writeByte((byte) (mDeleteEnabled ? 1 : 0)); } @Override public int getSpanTypeId() { return TextUtils.EASY_EDIT_SPAN; } + + /** + * @return True if the {@link TextView} should offer the ability to delete the text. + * + * @hide + */ + public boolean isDeleteEnabled() { + return mDeleteEnabled; + } + + /** + * Enables or disables the deletion of the text. + * + * @hide + */ + public void setDeleteEnabled(boolean value) { + mDeleteEnabled = value; + } + + /** + * @return the pending intent to send when the wrapped text is deleted or modified. + * + * @hide + */ + public PendingIntent getPendingIntent() { + return mPendingIntent; + } } diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 5dc206f..0ec7e84 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -17,6 +17,7 @@ package android.text.style; import android.content.Context; +import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Parcel; @@ -26,6 +27,7 @@ import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; import android.util.Log; +import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import java.util.Arrays; @@ -45,6 +47,8 @@ import java.util.Locale; */ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { + private static final String TAG = "SuggestionSpan"; + /** * Sets this flag if the suggestions should be easily accessible with few interactions. * This flag should be set for every suggestions that the user is likely to use. @@ -82,6 +86,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { private final String[] mSuggestions; private final String mLocaleString; private final String mNotificationTargetClassName; + private final String mNotificationTargetPackageName; private final int mHashCode; private float mEasyCorrectUnderlineThickness; @@ -134,6 +139,12 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { mLocaleString = ""; } + if (context != null) { + mNotificationTargetPackageName = context.getPackageName(); + } else { + mNotificationTargetPackageName = null; + } + if (notificationTargetClass != null) { mNotificationTargetClassName = notificationTargetClass.getCanonicalName(); } else { @@ -185,6 +196,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { mFlags = src.readInt(); mLocaleString = src.readString(); mNotificationTargetClassName = src.readString(); + mNotificationTargetPackageName = src.readString(); mHashCode = src.readInt(); mEasyCorrectUnderlineColor = src.readInt(); mEasyCorrectUnderlineThickness = src.readFloat(); @@ -240,6 +252,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { dest.writeInt(mFlags); dest.writeString(mLocaleString); dest.writeString(mNotificationTargetClassName); + dest.writeString(mNotificationTargetPackageName); dest.writeInt(mHashCode); dest.writeInt(mEasyCorrectUnderlineColor); dest.writeFloat(mEasyCorrectUnderlineThickness); @@ -325,4 +338,40 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } return 0; } + + /** + * Notifies a suggestion selection. + * + * @hide + */ + public void notifySelection(Context context, String original, int index) { + final Intent intent = new Intent(); + + if (context == null || mNotificationTargetClassName == null) { + return; + } + // Ensures that only a class in the original IME package will receive the + // notification. + if (mSuggestions == null || index < 0 || index >= mSuggestions.length) { + Log.w(TAG, "Unable to notify the suggestion as the index is out of range index=" + index + + " length=" + mSuggestions.length); + return; + } + + // The package name is not mandatory (legacy from JB), and if the package name + // is missing, we try to notify the suggestion through the input method manager. + if (mNotificationTargetPackageName != null) { + intent.setClassName(mNotificationTargetPackageName, mNotificationTargetClassName); + intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED); + intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, original); + intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, mSuggestions[index]); + intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode()); + context.sendBroadcast(intent); + } else { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.notifySuggestionPicked(this, original, index); + } + } + } } diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index e856501..dae47b8 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -74,6 +74,15 @@ public class DisplayMetrics { public static final int DENSITY_XXHIGH = 480; /** + * Standard quantized DPI for extra-extra-extra-high-density screens. Applications + * should not generally worry about this density; relying on XHIGH graphics + * being scaled up to it should be sufficient for almost all cases. A typical + * use of this density would be 4K television screens -- 3840x2160, which + * is 2x a traditional HD 1920x1080 screen which runs at DENSITY_XHIGH. + */ + public static final int DENSITY_XXXHIGH = 640; + + /** * The reference density used throughout the system. */ public static final int DENSITY_DEFAULT = DENSITY_MEDIUM; diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java deleted file mode 100644 index b30f2bf..0000000 --- a/core/java/android/util/FinitePool.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.util; - -/** - * @hide - */ -class FinitePool<T extends Poolable<T>> implements Pool<T> { - private static final String LOG_TAG = "FinitePool"; - - /** - * Factory used to create new pool objects - */ - private final PoolableManager<T> mManager; - /** - * Maximum number of objects in the pool - */ - private final int mLimit; - /** - * If true, mLimit is ignored - */ - private final boolean mInfinite; - - /** - * Next object to acquire - */ - private T mRoot; - /** - * Number of objects in the pool - */ - private int mPoolCount; - - FinitePool(PoolableManager<T> manager) { - mManager = manager; - mLimit = 0; - mInfinite = true; - } - - FinitePool(PoolableManager<T> manager, int limit) { - if (limit <= 0) throw new IllegalArgumentException("The pool limit must be > 0"); - - mManager = manager; - mLimit = limit; - mInfinite = false; - } - - public T acquire() { - T element; - - if (mRoot != null) { - element = mRoot; - mRoot = element.getNextPoolable(); - mPoolCount--; - } else { - element = mManager.newInstance(); - } - - if (element != null) { - element.setNextPoolable(null); - element.setPooled(false); - mManager.onAcquired(element); - } - - return element; - } - - public void release(T element) { - if (!element.isPooled()) { - if (mInfinite || mPoolCount < mLimit) { - mPoolCount++; - element.setNextPoolable(mRoot); - element.setPooled(true); - mRoot = element; - } - mManager.onReleased(element); - } else { - Log.w(LOG_TAG, "Element is already in pool: " + element); - } - } -} diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java new file mode 100644 index 0000000..34b6126 --- /dev/null +++ b/core/java/android/util/LongSparseLongArray.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import com.android.internal.util.ArrayUtils; + +import java.util.Arrays; + +/** + * Map of {@code long} to {@code long}. Unlike a normal array of longs, there + * can be gaps in the indices. It is intended to be more efficient than using a + * {@code HashMap}. + * + * @hide + */ +public class LongSparseLongArray implements Cloneable { + private long[] mKeys; + private long[] mValues; + private int mSize; + + /** + * Creates a new SparseLongArray containing no mappings. + */ + public LongSparseLongArray() { + this(10); + } + + /** + * Creates a new SparseLongArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public LongSparseLongArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); + + mKeys = new long[initialCapacity]; + mValues = new long[initialCapacity]; + mSize = 0; + } + + @Override + public LongSparseLongArray clone() { + LongSparseLongArray clone = null; + try { + clone = (LongSparseLongArray) super.clone(); + clone.mKeys = mKeys.clone(); + clone.mValues = mValues.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + + /** + * Gets the long mapped from the specified key, or <code>0</code> + * if no such mapping has been made. + */ + public long get(long key) { + return get(key, 0); + } + + /** + * Gets the long mapped from the specified key, or the specified value + * if no such mapping has been made. + */ + public long get(long key, long valueIfKeyNotFound) { + int i = Arrays.binarySearch(mKeys, 0, mSize, key); + + if (i < 0) { + return valueIfKeyNotFound; + } else { + return mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(long key) { + int i = Arrays.binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + removeAt(i); + } + } + + /** + * Removes the mapping at the given index. + */ + public void removeAt(int index) { + System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1)); + System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1)); + mSize--; + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(long key, long value) { + int i = Arrays.binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (mSize >= mKeys.length) { + growKeyAndValueArrays(mSize + 1); + } + + if (mSize - i != 0) { + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseIntArray + * currently stores. + */ + public int size() { + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseLongArray stores. + */ + public long keyAt(int index) { + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseLongArray stores. + */ + public long valueAt(int index) { + return mValues[index]; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(long key) { + return Arrays.binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(long value) { + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseIntArray. + */ + public void clear() { + mSize = 0; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(long key, long value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + int pos = mSize; + if (pos >= mKeys.length) { + growKeyAndValueArrays(pos + 1); + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private void growKeyAndValueArrays(int minNeededSize) { + int n = ArrayUtils.idealLongArraySize(minNeededSize); + + long[] nkeys = new long[n]; + long[] nvalues = new long[n]; + + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } +} diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java index 8edb3e6..70581be 100644 --- a/core/java/android/util/Pools.java +++ b/core/java/android/util/Pools.java @@ -17,25 +17,149 @@ package android.util; /** + * Helper class for crating pools of objects. An example use looks like this: + * <pre> + * public class MyPooledClass { + * + * private static final SynchronizedPool<MyPooledClass> sPool = + * new SynchronizedPool<MyPooledClass>(10); + * + * public static MyPooledClass obtain() { + * MyPooledClass instance = sPool.acquire(); + * return (instance != null) ? instance : new MyPooledClass(); + * } + * + * public void recycle() { + * // Clear state if needed. + * sPool.release(this); + * } + * + * . . . + * } + * </pre> + * * @hide */ -public class Pools { - private Pools() { - } +public final class Pools { + + /** + * Interface for managing a pool of objects. + * + * @param <T> The pooled type. + */ + public static interface Pool<T> { - public static <T extends Poolable<T>> Pool<T> simplePool(PoolableManager<T> manager) { - return new FinitePool<T>(manager); + /** + * @return An instance from the pool if such, null otherwise. + */ + public T acquire(); + + /** + * Release an instance to the pool. + * + * @param instance The instance to release. + * @return Whether the instance was put in the pool. + * + * @throws IllegalStateException If the instance is already in the pool. + */ + public boolean release(T instance); } - - public static <T extends Poolable<T>> Pool<T> finitePool(PoolableManager<T> manager, int limit) { - return new FinitePool<T>(manager, limit); + + private Pools() { + /* do nothing - hiding constructor */ } - public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool) { - return new SynchronizedPool<T>(pool); + /** + * Simple (non-synchronized) pool of objects. + * + * @param <T> The pooled type. + */ + public static class SimplePool<T> implements Pool<T> { + private final Object[] mPool; + + private int mPoolSize; + + /** + * Creates a new instance. + * + * @param maxPoolSize The max pool size. + * + * @throws IllegalArgumentException If the max pool size is less than zero. + */ + public SimplePool(int maxPoolSize) { + if (maxPoolSize <= 0) { + throw new IllegalArgumentException("The max pool size must be > 0"); + } + mPool = new Object[maxPoolSize]; + } + + @Override + @SuppressWarnings("unchecked") + public T acquire() { + if (mPoolSize > 0) { + final int lastPooledIndex = mPoolSize - 1; + T instance = (T) mPool[lastPooledIndex]; + mPool[lastPooledIndex] = null; + mPoolSize--; + return instance; + } + return null; + } + + @Override + public boolean release(T instance) { + if (isInPool(instance)) { + throw new IllegalStateException("Already in the pool!"); + } + if (mPoolSize < mPool.length) { + mPool[mPoolSize] = instance; + mPoolSize++; + return true; + } + return false; + } + + private boolean isInPool(T instance) { + for (int i = 0; i < mPoolSize; i++) { + if (mPool[i] == instance) { + return true; + } + } + return false; + } } - public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool, Object lock) { - return new SynchronizedPool<T>(pool, lock); + /** + * Synchronized) pool of objects. + * + * @param <T> The pooled type. + */ + public static class SynchronizedPool<T> extends SimplePool<T> { + private final Object mLock = new Object(); + + /** + * Creates a new instance. + * + * @param maxPoolSize The max pool size. + * + * @throws IllegalArgumentException If the max pool size is less than zero. + */ + public SynchronizedPool(int maxPoolSize) { + super(maxPoolSize); + } + + @Override + public T acquire() { + synchronized (mLock) { + return super.acquire(); + } + } + + @Override + public boolean release(T element) { + synchronized (mLock) { + return super.release(element); + } + } } } diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java new file mode 100755 index 0000000..eb9c47d --- /dev/null +++ b/core/java/android/util/PropertyValueModel.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +/** + * A value model for a {@link Property property} of a host object. This class can be used for + * both reflective and non-reflective property implementations. + * + * @param <H> the host type, where the host is the object that holds this property + * @param <T> the value type + * + * @see Property + * @see ValueModel + */ +public class PropertyValueModel<H, T> extends ValueModel<T> { + private final H mHost; + private final Property<H, T> mProperty; + + private PropertyValueModel(H host, Property<H, T> property) { + mProperty = property; + mHost = host; + } + + /** + * Returns the host. + * + * @return the host + */ + public H getHost() { + return mHost; + } + + /** + * Returns the property. + * + * @return the property + */ + public Property<H, T> getProperty() { + return mProperty; + } + + @Override + public Class<T> getType() { + return mProperty.getType(); + } + + @Override + public T get() { + return mProperty.get(mHost); + } + + @Override + public void set(T value) { + mProperty.set(mHost, value); + } + + /** + * Return an appropriate PropertyValueModel for this host and property. + * + * @param host the host + * @param property the property + * @return the value model + */ + public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) { + return new PropertyValueModel<H, T>(host, property); + } + + /** + * Return a PropertyValueModel for this {@code host} and a + * reflective property, constructed from this {@code propertyType} and {@code propertyName}. + * + * @param host + * @param propertyType the property type + * @param propertyName the property name + * @return a value model with this host and a reflective property with this type and name + * + * @see Property#of + */ + public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType, + String propertyName) { + return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName)); + } + + private static Class getNullaryMethodReturnType(Class c, String name) { + try { + return c.getMethod(name).getReturnType(); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static Class getFieldType(Class c, String name) { + try { + return c.getField(name).getType(); + } catch (NoSuchFieldException e) { + return null; + } + } + + private static String capitalize(String name) { + if (name.isEmpty()) { + return name; + } + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + /** + * Return a PropertyValueModel for this {@code host} and and {@code propertyName}. + * + * @param host the host + * @param propertyName the property name + * @return a value model with this host and a reflective property with this name + */ + public static PropertyValueModel of(Object host, String propertyName) { + Class clazz = host.getClass(); + String suffix = capitalize(propertyName); + Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix); + if (propertyType == null) { + propertyType = getNullaryMethodReturnType(clazz, "is" + suffix); + } + if (propertyType == null) { + propertyType = getFieldType(clazz, propertyName); + } + if (propertyType == null) { + throw new NoSuchPropertyException(propertyName); + } + return of(host, propertyType, propertyName); + } +} diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index a08d5cb..2f7a6fe 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -22,8 +22,6 @@ import com.android.internal.util.ArrayUtils; * SparseLongArrays map integers to longs. Unlike a normal array of longs, * there can be gaps in the indices. It is intended to be more efficient * than using a HashMap to map Integers to Longs. - * - * @hide */ public class SparseLongArray implements Cloneable { diff --git a/core/java/android/util/SynchronizedPool.java b/core/java/android/util/SynchronizedPool.java deleted file mode 100644 index 651e0c3..0000000 --- a/core/java/android/util/SynchronizedPool.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.util; - -/** - * - * @hide - */ -class SynchronizedPool<T extends Poolable<T>> implements Pool<T> { - private final Pool<T> mPool; - private final Object mLock; - - public SynchronizedPool(Pool<T> pool) { - mPool = pool; - mLock = this; - } - - public SynchronizedPool(Pool<T> pool, Object lock) { - mPool = pool; - mLock = lock; - } - - public T acquire() { - synchronized (mLock) { - return mPool.acquire(); - } - } - - public void release(T element) { - synchronized (mLock) { - mPool.release(element); - } - } -} diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java new file mode 100755 index 0000000..4789682 --- /dev/null +++ b/core/java/android/util/ValueModel.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +/** + * A ValueModel is an abstraction for a 'slot' or place in memory in which a value + * may be stored and retrieved. A common implementation of ValueModel is a regular property of + * an object, whose value may be retrieved by calling the appropriate <em>getter</em> + * method and set by calling the corresponding <em>setter</em> method. + * + * @param <T> the value type + * + * @see PropertyValueModel + */ +public abstract class ValueModel<T> { + /** + * The empty model should be used in place of {@code null} to indicate that a + * model has not been set. The empty model has no value and does nothing when it is set. + */ + public static final ValueModel EMPTY = new ValueModel() { + @Override + public Class getType() { + return Object.class; + } + + @Override + public Object get() { + return null; + } + + @Override + public void set(Object value) { + + } + }; + + protected ValueModel() { + } + + /** + * Returns the type of this property. + * + * @return the property type + */ + public abstract Class<T> getType(); + + /** + * Returns the value of this property. + * + * @return the property value + */ + public abstract T get(); + + /** + * Sets the value of this property. + * + * @param value the new value for this property + */ + public abstract void set(T value); +}
\ No newline at end of file diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 9bee4bf..2d6453e 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -16,8 +16,7 @@ package android.view; -import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS; - +import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -26,12 +25,14 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.util.SparseLongArray; +import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import com.android.internal.os.SomeArgs; +import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashMap; @@ -47,7 +48,7 @@ import java.util.Map; */ final class AccessibilityInteractionController { - private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = + private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); private final Handler mHandler; @@ -62,7 +63,12 @@ final class AccessibilityInteractionController { private final ArrayList<View> mTempArrayList = new ArrayList<View>(); + private final Point mTempPoint = new Point(); private final Rect mTempRect = new Rect(); + private final Rect mTempRect1 = new Rect(); + private final Rect mTempRect2 = new Rect(); + + private AddNodeInfosForViewId mAddNodeInfosForViewId; public AccessibilityInteractionController(ViewRootImpl viewRootImpl) { Looper looper = viewRootImpl.mHandler.getLooper(); @@ -86,7 +92,7 @@ final class AccessibilityInteractionController { public void findAccessibilityNodeInfoByAccessibilityIdClientThread( long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid) { + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; message.arg1 = flags; @@ -96,6 +102,7 @@ final class AccessibilityInteractionController { args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.argi3 = interactionId; args.arg1 = callback; + args.arg2 = spec; message.obj = args; // If the interrogation is performed by the same thread as the main UI @@ -119,6 +126,7 @@ final class AccessibilityInteractionController { final int interactionId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); @@ -128,8 +136,7 @@ final class AccessibilityInteractionController { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = - (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) { root = mViewRootImpl.mView; @@ -141,8 +148,11 @@ final class AccessibilityInteractionController { } } finally { try { - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(infos); + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfosResult(infos, interactionId); infos.clear(); } catch (RemoteException re) { @@ -151,18 +161,19 @@ final class AccessibilityInteractionController { } } - public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId, - int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid) { + public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, + String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, + int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); - message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; + message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID; message.arg1 = flags; message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); SomeArgs args = SomeArgs.obtain(); - args.argi1 = viewId; - args.argi2 = interactionId; + args.argi1 = interactionId; args.arg1 = callback; + args.arg2 = spec; + args.arg3 = viewId; message.obj = args; @@ -178,25 +189,26 @@ final class AccessibilityInteractionController { } } - private void findAccessibilityNodeInfoByViewIdUiThread(Message message) { + private void findAccessibilityNodeInfosByViewIdUiThread(Message message) { final int flags = message.arg1; final int accessibilityViewId = message.arg2; SomeArgs args = (SomeArgs) message.obj; - final int viewId = args.argi1; - final int interactionId = args.argi2; + final int interactionId = args.argi1; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; + final String viewId = (String) args.arg3; args.recycle(); - AccessibilityNodeInfo info = null; + final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; + infos.clear(); try { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = - (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { root = findViewByAccessibilityId(accessibilityViewId); @@ -204,16 +216,26 @@ final class AccessibilityInteractionController { root = mViewRootImpl.mView; } if (root != null) { - View target = root.findViewById(viewId); - if (target != null && isShown(target)) { - info = target.createAccessibilityNodeInfo(); + final int resolvedViewId = root.getContext().getResources() + .getIdentifier(viewId, null, null); + if (resolvedViewId <= 0) { + return; + } + if (mAddNodeInfosForViewId == null) { + mAddNodeInfosForViewId = new AddNodeInfosForViewId(); } + mAddNodeInfosForViewId.init(resolvedViewId, infos); + root.findViewByPredicate(mAddNodeInfosForViewId); + mAddNodeInfosForViewId.reset(); } } finally { try { - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(info); - callback.setFindAccessibilityNodeInfoResult(info, interactionId); + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); + if (spec != null) { + spec.recycle(); + } + callback.setFindAccessibilityNodeInfosResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ } @@ -222,7 +244,7 @@ final class AccessibilityInteractionController { public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid) { + int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT; message.arg1 = flags; @@ -230,10 +252,10 @@ final class AccessibilityInteractionController { SomeArgs args = SomeArgs.obtain(); args.arg1 = text; args.arg2 = callback; + args.arg3 = spec; args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.argi3 = interactionId; - message.obj = args; // If the interrogation is performed by the same thread as the main UI @@ -255,6 +277,7 @@ final class AccessibilityInteractionController { final String text = (String) args.arg1; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg2; + final MagnificationSpec spec = (MagnificationSpec) args.arg3; final int accessibilityViewId = args.argi1; final int virtualDescendantId = args.argi2; final int interactionId = args.argi3; @@ -265,8 +288,7 @@ final class AccessibilityInteractionController { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = - (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { root = findViewByAccessibilityId(accessibilityViewId); @@ -309,8 +331,11 @@ final class AccessibilityInteractionController { } } finally { try { - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(infos); + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfosResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -320,7 +345,7 @@ final class AccessibilityInteractionController { public void findFocusClientThread(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, - long interrogatingTid) { + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_FOCUS; message.arg1 = flags; @@ -331,6 +356,7 @@ final class AccessibilityInteractionController { args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.arg1 = callback; + args.arg2 = spec; message.obj = args; @@ -356,7 +382,7 @@ final class AccessibilityInteractionController { final int virtualDescendantId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; - + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); AccessibilityNodeInfo focused = null; @@ -364,8 +390,7 @@ final class AccessibilityInteractionController { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = - (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { root = findViewByAccessibilityId(accessibilityViewId); @@ -406,8 +431,11 @@ final class AccessibilityInteractionController { } } finally { try { - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(focused); + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + applyAppScaleAndMagnificationSpecIfNeeded(focused, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfoResult(focused, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -417,7 +445,7 @@ final class AccessibilityInteractionController { public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, - long interrogatingTid) { + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FOCUS_SEARCH; message.arg1 = flags; @@ -427,6 +455,7 @@ final class AccessibilityInteractionController { args.argi2 = direction; args.argi3 = interactionId; args.arg1 = callback; + args.arg2 = spec; message.obj = args; @@ -451,6 +480,7 @@ final class AccessibilityInteractionController { final int interactionId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); @@ -459,8 +489,7 @@ final class AccessibilityInteractionController { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = - (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { root = findViewByAccessibilityId(accessibilityViewId); @@ -475,8 +504,11 @@ final class AccessibilityInteractionController { } } finally { try { - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(next); + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + applyAppScaleAndMagnificationSpecIfNeeded(next, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfoResult(next, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -533,8 +565,7 @@ final class AccessibilityInteractionController { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = - (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View target = null; if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { target = findViewByAccessibilityId(accessibilityViewId); @@ -552,7 +583,7 @@ final class AccessibilityInteractionController { } } finally { try { - mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; callback.setPerformAccessibilityActionResult(succeeded, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -572,38 +603,84 @@ final class AccessibilityInteractionController { return foundView; } - private void applyApplicationScaleIfNeeded(List<AccessibilityNodeInfo> infos) { + private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos, + MagnificationSpec spec) { if (infos == null) { return; } final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; - if (applicationScale != 1.0f) { + if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { AccessibilityNodeInfo info = infos.get(i); - applyApplicationScaleIfNeeded(info); + applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } } } - private void applyApplicationScaleIfNeeded(AccessibilityNodeInfo info) { + private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info, + MagnificationSpec spec) { if (info == null) { return; } + final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; + if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { + return; + } + + Rect boundsInParent = mTempRect; + Rect boundsInScreen = mTempRect1; + + info.getBoundsInParent(boundsInParent); + info.getBoundsInScreen(boundsInScreen); if (applicationScale != 1.0f) { - Rect bounds = mTempRect; + boundsInParent.scale(applicationScale); + boundsInScreen.scale(applicationScale); + } + if (spec != null) { + boundsInParent.scale(spec.scale); + // boundsInParent must not be offset. + boundsInScreen.scale(spec.scale); + boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY); + } + info.setBoundsInParent(boundsInParent); + info.setBoundsInScreen(boundsInScreen); + + if (spec != null) { + AttachInfo attachInfo = mViewRootImpl.mAttachInfo; + if (attachInfo.mDisplay == null) { + return; + } - info.getBoundsInParent(bounds); - bounds.scale(applicationScale); - info.setBoundsInParent(bounds); + final float scale = attachInfo.mApplicationScale * spec.scale; - info.getBoundsInScreen(bounds); - bounds.scale(applicationScale); - info.setBoundsInScreen(bounds); + Rect visibleWinFrame = mTempRect1; + visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX); + visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY); + visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale); + visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale); + + attachInfo.mDisplay.getRealSize(mTempPoint); + final int displayWidth = mTempPoint.x; + final int displayHeight = mTempPoint.y; + + Rect visibleDisplayFrame = mTempRect2; + visibleDisplayFrame.set(0, 0, displayWidth, displayHeight); + + visibleWinFrame.intersect(visibleDisplayFrame); + + if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top, + boundsInScreen.right, boundsInScreen.bottom)) { + info.setVisibleToUser(false); + } } } + private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale, + MagnificationSpec spec) { + return (appScale != 1.0f || (spec != null && !spec.isNop())); + } /** * This class encapsulates a prefetching strategy for the accessibility APIs for @@ -616,20 +693,20 @@ final class AccessibilityInteractionController { private final ArrayList<View> mTempViewList = new ArrayList<View>(); - public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags, + public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) { AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); if (provider == null) { AccessibilityNodeInfo root = view.createAccessibilityNodeInfo(); if (root != null) { outInfos.add(root); - if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { prefetchPredecessorsOfRealNode(view, outInfos); } - if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { prefetchSiblingsOfRealNode(view, outInfos); } - if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { prefetchDescendantsOfRealNode(view, outInfos); } } @@ -637,13 +714,13 @@ final class AccessibilityInteractionController { AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId); if (root != null) { outInfos.add(root); - if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos); } - if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { prefetchSiblingsOfVirtualNode(root, view, provider, outInfos); } - if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { prefetchDescendantsOfVirtualNode(root, provider, outInfos); } } @@ -846,7 +923,7 @@ final class AccessibilityInteractionController { private class PrivateHandler extends Handler { private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1; private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2; - private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 3; + private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3; private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4; private final static int MSG_FIND_FOCUS = 5; private final static int MSG_FOCUS_SEARCH = 6; @@ -863,8 +940,8 @@ final class AccessibilityInteractionController { return "MSG_PERFORM_ACCESSIBILITY_ACTION"; case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; - case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: - return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; + case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: + return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID"; case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT"; case MSG_FIND_FOCUS: @@ -886,8 +963,8 @@ final class AccessibilityInteractionController { case MSG_PERFORM_ACCESSIBILITY_ACTION: { perfromAccessibilityActionUiThread(message); } break; - case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { - findAccessibilityNodeInfoByViewIdUiThread(message); + case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: { + findAccessibilityNodeInfosByViewIdUiThread(message); } break; case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: { findAccessibilityNodeInfosByTextUiThread(message); @@ -903,4 +980,27 @@ final class AccessibilityInteractionController { } } } + + private final class AddNodeInfosForViewId implements Predicate<View> { + private int mViewId = View.NO_ID; + private List<AccessibilityNodeInfo> mInfos; + + public void init(int viewId, List<AccessibilityNodeInfo> infos) { + mViewId = viewId; + mInfos = infos; + } + + public void reset() { + mViewId = View.NO_ID; + mInfos = null; + } + + @Override + public boolean apply(View view) { + if (view.getId() == mViewId && isShown(view)) { + mInfos.add(view.createAccessibilityNodeInfo()); + } + return false; + } + } } diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index b661748..f28e4b5 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -693,7 +693,7 @@ public final class Choreographer { // At this time Surface Flinger won't send us vsyncs for secondary displays // but that could change in the future so let's log a message to help us remember // that we need to fix this. - if (builtInDisplayId != Surface.BUILT_IN_DISPLAY_ID_MAIN) { + if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { Log.d(TAG, "Received vsync from secondary display, but we don't support " + "this case yet. Choreographer needs a way to explicitly request " + "vsync for a specific display to ensure it doesn't lose track " diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 758abb5..e6a7950 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -437,6 +437,20 @@ public final class Display { } /** + * @hide + * Return a rectangle defining the insets of the overscan region of the display. + * Each field of the rectangle is the number of pixels the overscan area extends + * into the display on that side. + */ + public void getOverscanInsets(Rect outRect) { + synchronized (this) { + updateDisplayInfoLocked(); + outRect.set(mDisplayInfo.overscanLeft, mDisplayInfo.overscanTop, + mDisplayInfo.overscanRight, mDisplayInfo.overscanBottom); + } + } + + /** * Returns the rotation of the screen from its "natural" orientation. * The returned value may be {@link Surface#ROTATION_0 Surface.ROTATION_0} * (no rotation), {@link Surface#ROTATION_90 Surface.ROTATION_90}, diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index a919ffc..4dade20 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -102,7 +102,7 @@ public abstract class DisplayEventReceiver { * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()} * timebase. * @param builtInDisplayId The surface flinger built-in display id such as - * {@link Surface#BUILT_IN_DISPLAY_ID_MAIN}. + * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}. * @param frame The frame number. Increases by one for each vertical sync interval. */ public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { @@ -114,7 +114,7 @@ public abstract class DisplayEventReceiver { * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. * @param builtInDisplayId The surface flinger built-in display id such as - * {@link Surface#BUILT_IN_DISPLAY_ID_HDMI}. + * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_HDMI}. * @param connected True if the display is connected, false if it disconnected. */ public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 305fd5c..9fcd9b1 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -109,6 +109,30 @@ public final class DisplayInfo implements Parcelable { public int logicalHeight; /** + * @hide + * Number of overscan pixels on the left side of the display. + */ + public int overscanLeft; + + /** + * @hide + * Number of overscan pixels on the top side of the display. + */ + public int overscanTop; + + /** + * @hide + * Number of overscan pixels on the right side of the display. + */ + public int overscanRight; + + /** + * @hide + * Number of overscan pixels on the bottom side of the display. + */ + public int overscanBottom; + + /** * The rotation of the display relative to its natural orientation. * May be one of {@link android.view.Surface#ROTATION_0}, * {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180}, @@ -196,6 +220,10 @@ public final class DisplayInfo implements Parcelable { && largestNominalAppHeight == other.largestNominalAppHeight && logicalWidth == other.logicalWidth && logicalHeight == other.logicalHeight + && overscanLeft == other.overscanLeft + && overscanTop == other.overscanTop + && overscanRight == other.overscanRight + && overscanBottom == other.overscanBottom && rotation == other.rotation && refreshRate == other.refreshRate && logicalDensityDpi == other.logicalDensityDpi @@ -222,6 +250,10 @@ public final class DisplayInfo implements Parcelable { largestNominalAppHeight = other.largestNominalAppHeight; logicalWidth = other.logicalWidth; logicalHeight = other.logicalHeight; + overscanLeft = other.overscanLeft; + overscanTop = other.overscanTop; + overscanRight = other.overscanRight; + overscanBottom = other.overscanBottom; rotation = other.rotation; refreshRate = other.refreshRate; logicalDensityDpi = other.logicalDensityDpi; @@ -243,6 +275,10 @@ public final class DisplayInfo implements Parcelable { largestNominalAppHeight = source.readInt(); logicalWidth = source.readInt(); logicalHeight = source.readInt(); + overscanLeft = source.readInt(); + overscanTop = source.readInt(); + overscanRight = source.readInt(); + overscanBottom = source.readInt(); rotation = source.readInt(); refreshRate = source.readFloat(); logicalDensityDpi = source.readInt(); @@ -265,6 +301,10 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(largestNominalAppHeight); dest.writeInt(logicalWidth); dest.writeInt(logicalHeight); + dest.writeInt(overscanLeft); + dest.writeInt(overscanTop); + dest.writeInt(overscanRight); + dest.writeInt(overscanBottom); dest.writeInt(rotation); dest.writeFloat(refreshRate); dest.writeInt(logicalDensityDpi); @@ -318,18 +358,55 @@ public final class DisplayInfo implements Parcelable { // For debugging purposes @Override public String toString() { - return "DisplayInfo{\"" + name + "\", app " + appWidth + " x " + appHeight - + ", real " + logicalWidth + " x " + logicalHeight - + ", largest app " + largestNominalAppWidth + " x " + largestNominalAppHeight - + ", smallest app " + smallestNominalAppWidth + " x " + smallestNominalAppHeight - + ", " + refreshRate + " fps" - + ", rotation " + rotation - + ", density " + logicalDensityDpi - + ", " + physicalXDpi + " x " + physicalYDpi + " dpi" - + ", layerStack " + layerStack - + ", type " + Display.typeToString(type) - + ", address " + address - + flagsToString(flags) + "}"; + StringBuilder sb = new StringBuilder(); + sb.append("DisplayInfo{\""); + sb.append(name); + sb.append("\", app "); + sb.append(appWidth); + sb.append(" x "); + sb.append(appHeight); + sb.append(", real "); + sb.append(logicalWidth); + sb.append(" x "); + sb.append(logicalHeight); + if (overscanLeft != 0 || overscanTop != 0 || overscanRight != 0 || overscanBottom != 0) { + sb.append(", overscan ("); + sb.append(overscanLeft); + sb.append(","); + sb.append(overscanTop); + sb.append(","); + sb.append(overscanRight); + sb.append(","); + sb.append(overscanBottom); + sb.append(")"); + } + sb.append(", largest app "); + sb.append(largestNominalAppWidth); + sb.append(" x "); + sb.append(largestNominalAppHeight); + sb.append(", smallest app "); + sb.append(smallestNominalAppWidth); + sb.append(" x "); + sb.append(smallestNominalAppHeight); + sb.append(", "); + sb.append(refreshRate); + sb.append(" fps, rotation"); + sb.append(rotation); + sb.append(", density "); + sb.append(logicalDensityDpi); + sb.append(" ("); + sb.append(physicalXDpi); + sb.append(" x "); + sb.append(physicalYDpi); + sb.append(") dpi, layerStack "); + sb.append(layerStack); + sb.append(", type "); + sb.append(Display.typeToString(type)); + sb.append(", address "); + sb.append(address); + sb.append(flagsToString(flags)); + sb.append("}"); + return sb.toString(); } private static String flagsToString(int flags) { diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index 5e34a36..3bad98e 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -19,21 +19,121 @@ package android.view; import android.graphics.Matrix; /** - * A display lists records a series of graphics related operation and can replay + * <p>A display list records a series of graphics related operations and can replay * them later. Display lists are usually built by recording operations on a - * {@link android.graphics.Canvas}. Replaying the operations from a display list - * avoids executing views drawing code on every frame, and is thus much more - * efficient. + * {@link HardwareCanvas}. Replaying the operations from a display list avoids + * executing application code on every frame, and is thus much more efficient.</p> * - * @hide + * <p>Display lists are used internally for all views by default, and are not + * typically used directly. One reason to consider using a display is a custom + * {@link View} implementation that needs to issue a large number of drawing commands. + * When the view invalidates, all the drawing commands must be reissued, even if + * large portions of the drawing command stream stay the same frame to frame, which + * can become a performance bottleneck. To solve this issue, a custom View might split + * its content into several display lists. A display list is updated only when its + * content, and only its content, needs to be updated.</p> + * + * <p>A text editor might for instance store each paragraph into its own display list. + * Thus when the user inserts or removes characters, only the display list of the + * affected paragraph needs to be recorded again.</p> + * + * <h3>Hardware acceleration</h3> + * <p>Display lists can only be replayed using a {@link HardwareCanvas}. They are not + * supported in software. Always make sure that the {@link android.graphics.Canvas} + * you are using to render a display list is hardware accelerated using + * {@link android.graphics.Canvas#isHardwareAccelerated()}.</p> + * + * <h3>Creating a display list</h3> + * <pre class="prettyprint"> + * HardwareRenderer renderer = myView.getHardwareRenderer(); + * if (renderer != null) { + * DisplayList displayList = renderer.createDisplayList(); + * HardwareCanvas canvas = displayList.start(width, height); + * try { + * // Draw onto the canvas + * // For instance: canvas.drawBitmap(...); + * } finally { + * displayList.end(); + * } + * } + * </pre> + * + * <h3>Rendering a display list on a View</h3> + * <pre class="prettyprint"> + * protected void onDraw(Canvas canvas) { + * if (canvas.isHardwareAccelerated()) { + * HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas; + * hardwareCanvas.drawDisplayList(mDisplayList); + * } + * } + * </pre> + * + * <h3>Releasing resources</h3> + * <p>This step is not mandatory but recommended if you want to release resources + * held by a display list as soon as possible.</p> + * <pre class="prettyprint"> + * // Mark this display list invalid, it cannot be used for drawing anymore, + * // and release resources held by this display list + * displayList.clear(); + * </pre> + * + * <h3>Properties</h3> + * <p>In addition, a display list offers several properties, such as + * {@link #setScaleX(float)} or {@link #setLeft(int)}, that can be used to affect all + * the drawing commands recorded within. For instance, these properties can be used + * to move around a large number of images without re-issuing all the individual + * <code>drawBitmap()</code> calls.</p> + * + * <pre class="prettyprint"> + * private void createDisplayList() { + * HardwareRenderer renderer = getHardwareRenderer(); + * if (renderer != null) { + * mDisplayList = renderer.createDisplayList(); + * HardwareCanvas canvas = mDisplayList.start(width, height); + * try { + * for (Bitmap b : mBitmaps) { + * canvas.drawBitmap(b, 0.0f, 0.0f, null); + * canvas.translate(0.0f, b.getHeight()); + * } + * } finally { + * displayList.end(); + * } + * } + * } + * + * protected void onDraw(Canvas canvas) { + * if (canvas.isHardwareAccelerated()) { + * HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas; + * hardwareCanvas.drawDisplayList(mDisplayList); + * } + * } + * + * private void moveContentBy(int x) { + * // This will move all the bitmaps recorded inside the display list + * // by x pixels to the right and redraw this view. All the commands + * // recorded in createDisplayList() won't be re-issued, only onDraw() + * // will be invoked and will execute very quickly + * mDisplayList.offsetLeftAndRight(x); + * invalidate(); + * } + * </pre> + * + * <h3>Threading</h3> + * <p>Display lists must be created on and manipulated from the UI thread only.</p> + * + * @hide */ public abstract class DisplayList { + private boolean mDirty; + /** * Flag used when calling * {@link HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int)} * When this flag is set, draw operations lying outside of the bounds of the * display list will be culled early. It is recommeneded to always set this * flag. + * + * @hide */ public static final int FLAG_CLIP_CHILDREN = 0x1; @@ -42,14 +142,18 @@ public abstract class DisplayList { /** * Indicates that the display list is done drawing. * - * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) + * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) + * + * @hide */ public static final int STATUS_DONE = 0x0; /** * Indicates that the display list needs another drawing pass. * - * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) + * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) + * + * @hide */ public static final int STATUS_DRAW = 0x1; @@ -57,7 +161,9 @@ public abstract class DisplayList { * Indicates that the display list needs to re-execute its GL functors. * * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) - * @see HardwareCanvas#callDrawGLFunction(int) + * @see HardwareCanvas#callDrawGLFunction(int) + * + * @hide */ public static final int STATUS_INVOKE = 0x2; @@ -65,35 +171,83 @@ public abstract class DisplayList { * Indicates that the display list performed GL drawing operations. * * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) + * + * @hide */ public static final int STATUS_DREW = 0x4; /** * Starts recording the display list. All operations performed on the * returned canvas are recorded and stored in this display list. - * + * + * Calling this method will mark the display list invalid until + * {@link #end()} is called. Only valid display lists can be replayed. + * + * @param width The width of the display list's viewport + * @param height The height of the display list's viewport + * * @return A canvas to record drawing operations. + * + * @see #end() + * @see #isValid() */ - public abstract HardwareCanvas start(); + public abstract HardwareCanvas start(int width, int height); /** * Ends the recording for this display list. A display list cannot be - * replayed if recording is not finished. + * replayed if recording is not finished. Calling this method marks + * the display list valid and {@link #isValid()} will return true. + * + * @see #start(int, int) + * @see #isValid() */ public abstract void end(); /** - * Invalidates the display list, indicating that it should be repopulated - * with new drawing commands prior to being used again. Calling this method - * causes calls to {@link #isValid()} to return <code>false</code>. + * Clears resources held onto by this display list. After calling this method + * {@link #isValid()} will return false. + * + * @see #isValid() + */ + public abstract void clear(); + + /** + * Sets the dirty flag. When a display list is dirty, {@link #clear()} should + * be invoked whenever possible. + * + * @see #isDirty() + * @see #clear() + * + * @hide */ - public abstract void invalidate(); + public void markDirty() { + mDirty = true; + } /** - * Clears additional resources held onto by this display list. You should - * only invoke this method after {@link #invalidate()}. + * Removes the dirty flag. This method can be used to cancel a cleanup + * previously scheduled by setting the dirty flag. + * + * @see #isDirty() + * @see #clear() + * + * @hide */ - public abstract void clear(); + protected void clearDirty() { + mDirty = false; + } + + /** + * Indicates whether the display list is dirty. + * + * @see #markDirty() + * @see #clear() + * + * @hide + */ + public boolean isDirty() { + return mDirty; + } /** * Returns whether the display list is currently usable. If this returns false, @@ -107,6 +261,8 @@ public abstract class DisplayList { * Return the amount of memory used by this display list. * * @return The size of this display list in bytes + * + * @hide */ public abstract int getSize(); @@ -115,228 +271,412 @@ public abstract class DisplayList { /////////////////////////////////////////////////////////////////////////// /** - * Set the caching property on the DisplayList, which indicates whether the DisplayList - * holds a layer. Layer DisplayLists should avoid creating an alpha layer, since alpha is + * Set the caching property on the display list, which indicates whether the display list + * holds a layer. Layer display lists should avoid creating an alpha layer, since alpha is * handled in the drawLayer operation directly (and more efficiently). * - * @param caching true if the DisplayList represents a hardware layer, false otherwise. + * @param caching true if the display list represents a hardware layer, false otherwise. + * + * @hide */ public abstract void setCaching(boolean caching); /** - * Set whether the DisplayList should clip itself to its bounds. This property is controlled by + * Set whether the display list should clip itself to its bounds. This property is controlled by * the view's parent. * - * @param clipChildren true if the DisplayList should clip to its bounds + * @param clipChildren true if the display list should clip to its bounds */ public abstract void setClipChildren(boolean clipChildren); /** - * Set the static matrix on the DisplayList. This matrix exists if a custom ViewGroup - * overrides - * {@link ViewGroup#getChildStaticTransformation(View, android.view.animation.Transformation)} - * and also has {@link ViewGroup#setStaticTransformationsEnabled(boolean)} set to true. - * This matrix will be concatenated with any other matrices in the DisplayList to position - * the view appropriately. + * Set the static matrix on the display list. The specified matrix is combined with other + * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.) + * + * @param matrix A transform matrix to apply to this display list + * + * @see #getMatrix(android.graphics.Matrix) + * @see #getMatrix() + */ + public abstract void setMatrix(Matrix matrix); + + /** + * Returns the static matrix set on this display list. + * + * @return A new {@link Matrix} instance populated with this display list's static + * matrix + * + * @see #getMatrix(android.graphics.Matrix) + * @see #setMatrix(android.graphics.Matrix) + */ + public Matrix getMatrix() { + return getMatrix(new Matrix()); + } + + /** + * Copies this display list's static matrix into the specified matrix. + * + * @param matrix The {@link Matrix} instance in which to copy this display + * list's static matrix. Cannot be null * - * @param matrix The matrix + * @return The <code>matrix</code> parameter, for convenience + * + * @see #getMatrix() + * @see #setMatrix(android.graphics.Matrix) */ - public abstract void setStaticMatrix(Matrix matrix); + public abstract Matrix getMatrix(Matrix matrix); /** - * Set the Animation matrix on the DisplayList. This matrix exists if an Animation is - * currently playing on a View, and is set on the DisplayList during at draw() time. When + * Set the Animation matrix on the display list. This matrix exists if an Animation is + * currently playing on a View, and is set on the display list during at draw() time. When * the Animation finishes, the matrix should be cleared by sending <code>null</code> * for the matrix parameter. * * @param matrix The matrix, null indicates that the matrix should be cleared. + * + * @hide */ public abstract void setAnimationMatrix(Matrix matrix); /** - * Sets the alpha value for the DisplayList + * Sets the translucency level for the display list. + * + * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f * - * @param alpha The translucency of the DisplayList * @see View#setAlpha(float) + * @see #getAlpha() */ public abstract void setAlpha(float alpha); /** - * Sets whether the DisplayList renders content which overlaps. Non-overlapping rendering - * can use a fast path for alpha that avoids rendering to an offscreen buffer. + * Returns the translucency level of this display list. + * + * @return A value between 0.0f and 1.0f + * + * @see #setAlpha(float) + */ + public abstract float getAlpha(); + + /** + * Sets whether the display list renders content which overlaps. Non-overlapping rendering + * can use a fast path for alpha that avoids rendering to an offscreen buffer. By default + * display lists consider they do not have overlapping content. + * + * @param hasOverlappingRendering False if the content is guaranteed to be non-overlapping, + * true otherwise. * - * @param hasOverlappingRendering * @see android.view.View#hasOverlappingRendering() + * @see #hasOverlappingRendering() */ public abstract void setHasOverlappingRendering(boolean hasOverlappingRendering); /** - * Sets the translationX value for the DisplayList + * Indicates whether the content of this display list overlaps. + * + * @return True if this display list renders content which overlaps, false otherwise. + * + * @see #setHasOverlappingRendering(boolean) + */ + public abstract boolean hasOverlappingRendering(); + + /** + * Sets the translation value for the display list on the X axis + * + * @param translationX The X axis translation value of the display list, in pixels * - * @param translationX The translationX value of the DisplayList * @see View#setTranslationX(float) + * @see #getTranslationX() */ public abstract void setTranslationX(float translationX); /** - * Sets the translationY value for the DisplayList + * Returns the translation value for this display list on the X axis, in pixels. + * + * @see #setTranslationX(float) + */ + public abstract float getTranslationX(); + + /** + * Sets the translation value for the display list on the Y axis + * + * @param translationY The Y axis translation value of the display list, in pixels * - * @param translationY The translationY value of the DisplayList * @see View#setTranslationY(float) + * @see #getTranslationY() */ public abstract void setTranslationY(float translationY); /** - * Sets the rotation value for the DisplayList + * Returns the translation value for this display list on the Y axis, in pixels. + * + * @see #setTranslationY(float) + */ + public abstract float getTranslationY(); + + /** + * Sets the rotation value for the display list around the Z axis + * + * @param rotation The rotation value of the display list, in degrees * - * @param rotation The rotation value of the DisplayList * @see View#setRotation(float) + * @see #getRotation() */ public abstract void setRotation(float rotation); /** - * Sets the rotationX value for the DisplayList + * Returns the rotation value for this display list around the Z axis, in degrees. + * + * @see #setRotation(float) + */ + public abstract float getRotation(); + + /** + * Sets the rotation value for the display list around the X axis + * + * @param rotationX The rotation value of the display list, in degrees * - * @param rotationX The rotationX value of the DisplayList * @see View#setRotationX(float) + * @see #getRotationX() */ public abstract void setRotationX(float rotationX); /** - * Sets the rotationY value for the DisplayList + * Returns the rotation value for this display list around the X axis, in degrees. + * + * @see #setRotationX(float) + */ + public abstract float getRotationX(); + + /** + * Sets the rotation value for the display list around the Y axis + * + * @param rotationY The rotation value of the display list, in degrees * - * @param rotationY The rotationY value of the DisplayList * @see View#setRotationY(float) + * @see #getRotationY() */ public abstract void setRotationY(float rotationY); /** - * Sets the scaleX value for the DisplayList + * Returns the rotation value for this display list around the Y axis, in degrees. + * + * @see #setRotationY(float) + */ + public abstract float getRotationY(); + + /** + * Sets the scale value for the display list on the X axis + * + * @param scaleX The scale value of the display list * - * @param scaleX The scaleX value of the DisplayList * @see View#setScaleX(float) + * @see #getScaleX() */ public abstract void setScaleX(float scaleX); /** - * Sets the scaleY value for the DisplayList + * Returns the scale value for this display list on the X axis. + * + * @see #setScaleX(float) + */ + public abstract float getScaleX(); + + /** + * Sets the scale value for the display list on the Y axis + * + * @param scaleY The scale value of the display list * - * @param scaleY The scaleY value of the DisplayList * @see View#setScaleY(float) + * @see #getScaleY() */ public abstract void setScaleY(float scaleY); /** - * Sets all of the transform-related values of the View onto the DisplayList + * Returns the scale value for this display list on the Y axis. * - * @param alpha The alpha value of the DisplayList - * @param translationX The translationX value of the DisplayList - * @param translationY The translationY value of the DisplayList - * @param rotation The rotation value of the DisplayList - * @param rotationX The rotationX value of the DisplayList - * @param rotationY The rotationY value of the DisplayList - * @param scaleX The scaleX value of the DisplayList - * @param scaleY The scaleY value of the DisplayList + * @see #setScaleY(float) + */ + public abstract float getScaleY(); + + /** + * Sets all of the transform-related values of the display list + * + * @param alpha The alpha value of the display list + * @param translationX The translationX value of the display list + * @param translationY The translationY value of the display list + * @param rotation The rotation value of the display list + * @param rotationX The rotationX value of the display list + * @param rotationY The rotationY value of the display list + * @param scaleX The scaleX value of the display list + * @param scaleY The scaleY value of the display list + * + * @hide */ public abstract void setTransformationInfo(float alpha, float translationX, float translationY, float rotation, float rotationX, float rotationY, float scaleX, float scaleY); /** - * Sets the pivotX value for the DisplayList + * Sets the pivot value for the display list on the X axis + * + * @param pivotX The pivot value of the display list on the X axis, in pixels * - * @param pivotX The pivotX value of the DisplayList * @see View#setPivotX(float) + * @see #getPivotX() */ public abstract void setPivotX(float pivotX); /** - * Sets the pivotY value for the DisplayList + * Returns the pivot value for this display list on the X axis, in pixels. + * + * @see #setPivotX(float) + */ + public abstract float getPivotX(); + + /** + * Sets the pivot value for the display list on the Y axis + * + * @param pivotY The pivot value of the display list on the Y axis, in pixels * - * @param pivotY The pivotY value of the DisplayList * @see View#setPivotY(float) + * @see #getPivotY() */ public abstract void setPivotY(float pivotY); /** - * Sets the camera distance for the DisplayList + * Returns the pivot value for this display list on the Y axis, in pixels. + * + * @see #setPivotY(float) + */ + public abstract float getPivotY(); + + /** + * Sets the camera distance for the display list. Refer to + * {@link View#setCameraDistance(float)} for more information on how to + * use this property. + * + * @param distance The distance in Z of the camera of the display list * - * @param distance The distance in z of the camera of the DisplayList * @see View#setCameraDistance(float) + * @see #getCameraDistance() */ public abstract void setCameraDistance(float distance); /** - * Sets the left value for the DisplayList + * Returns the distance in Z of the camera of the display list. + * + * @see #setCameraDistance(float) + */ + public abstract float getCameraDistance(); + + /** + * Sets the left position for the display list. + * + * @param left The left position, in pixels, of the display list * - * @param left The left value of the DisplayList * @see View#setLeft(int) + * @see #getLeft() */ public abstract void setLeft(int left); /** - * Sets the top value for the DisplayList + * Returns the left position for the display list in pixels. + * + * @see #setLeft(int) + */ + public abstract float getLeft(); + + /** + * Sets the top position for the display list. + * + * @param top The top position, in pixels, of the display list * - * @param top The top value of the DisplayList * @see View#setTop(int) + * @see #getTop() */ public abstract void setTop(int top); /** - * Sets the right value for the DisplayList + * Returns the top position for the display list in pixels. + * + * @see #setTop(int) + */ + public abstract float getTop(); + + /** + * Sets the right position for the display list. + * + * @param right The right position, in pixels, of the display list * - * @param right The right value of the DisplayList * @see View#setRight(int) + * @see #getRight() */ public abstract void setRight(int right); /** - * Sets the bottom value for the DisplayList + * Returns the right position for the display list in pixels. + * + * @see #setRight(int) + */ + public abstract float getRight(); + + /** + * Sets the bottom position for the display list. + * + * @param bottom The bottom position, in pixels, of the display list * - * @param bottom The bottom value of the DisplayList * @see View#setBottom(int) + * @see #getBottom() */ public abstract void setBottom(int bottom); /** - * Sets the left and top values for the DisplayList + * Returns the bottom position for the display list in pixels. * - * @param left The left value of the DisplayList - * @param top The top value of the DisplayList - * @see View#setLeft(int) - * @see View#setTop(int) + * @see #setBottom(int) */ - public abstract void setLeftTop(int left, int top); + public abstract float getBottom(); /** - * Sets the left and top values for the DisplayList + * Sets the left and top positions for the display list + * + * @param left The left position of the display list, in pixels + * @param top The top position of the display list, in pixels + * @param right The right position of the display list, in pixels + * @param bottom The bottom position of the display list, in pixels * - * @param left The left value of the DisplayList - * @param top The top value of the DisplayList * @see View#setLeft(int) * @see View#setTop(int) + * @see View#setRight(int) + * @see View#setBottom(int) */ public abstract void setLeftTopRightBottom(int left, int top, int right, int bottom); /** - * Offsets the left and right values for the DisplayList + * Offsets the left and right positions for the display list + * + * @param offset The amount that the left and right positions of the display + * list are offset, in pixels * - * @param offset The amount that the left and right values of the DisplayList are offset * @see View#offsetLeftAndRight(int) */ - public abstract void offsetLeftRight(int offset); + public abstract void offsetLeftAndRight(float offset); /** - * Offsets the top and bottom values for the DisplayList + * Offsets the top and bottom values for the display list + * + * @param offset The amount that the top and bottom positions of the display + * list are offset, in pixels * - * @param offset The amount that the top and bottom values of the DisplayList are offset * @see View#offsetTopAndBottom(int) */ - public abstract void offsetTopBottom(int offset); + public abstract void offsetTopAndBottom(float offset); /** - * Reset native resources. This is called when cleaning up the state of DisplayLists + * Reset native resources. This is called when cleaning up the state of display lists * during destruction of hardware resources, to ensure that we do not hold onto * obsolete resources after related resources are gone. + * + * @hide */ public abstract void reset(); } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index b64a06e..7ff8d09 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -144,6 +144,14 @@ class GLES20Canvas extends HardwareCanvas { } } + @Override + public void setName(String name) { + super.setName(name); + nSetName(mRenderer, name); + } + + private static native void nSetName(int renderer, String name); + /////////////////////////////////////////////////////////////////////////// // Hardware layers /////////////////////////////////////////////////////////////////////////// @@ -369,24 +377,13 @@ class GLES20Canvas extends HardwareCanvas { } private static native int nGetDisplayList(int renderer, int displayList); - - static void destroyDisplayList(int displayList) { - nDestroyDisplayList(displayList); - } - private static native void nDestroyDisplayList(int displayList); - - static int getDisplayListSize(int displayList) { - return nGetDisplayListSize(displayList); - } - - private static native int nGetDisplayListSize(int displayList); - - static void setDisplayListName(int displayList, String name) { - nSetDisplayListName(displayList, name); + @Override + void outputDisplayList(DisplayList displayList) { + nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList()); } - private static native void nSetDisplayListName(int displayList, String name); + private static native void nOutputDisplayList(int renderer, int displayList); @Override public int drawDisplayList(DisplayList displayList, Rect dirty, int flags) { @@ -397,13 +394,6 @@ class GLES20Canvas extends HardwareCanvas { private static native int nDrawDisplayList(int renderer, int displayList, Rect dirty, int flags); - @Override - void outputDisplayList(DisplayList displayList) { - nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList()); - } - - private static native void nOutputDisplayList(int renderer, int displayList); - /////////////////////////////////////////////////////////////////////////// // Hardware layer /////////////////////////////////////////////////////////////////////////// @@ -433,20 +423,16 @@ class GLES20Canvas extends HardwareCanvas { @Override public boolean clipPath(Path path) { - // TODO: Implement - path.computeBounds(mPathBounds, true); - return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top, - mPathBounds.right, mPathBounds.bottom, Region.Op.INTERSECT.nativeInt); + return nClipPath(mRenderer, path.mNativePath, Region.Op.INTERSECT.nativeInt); } @Override public boolean clipPath(Path path, Region.Op op) { - // TODO: Implement - path.computeBounds(mPathBounds, true); - return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top, - mPathBounds.right, mPathBounds.bottom, op.nativeInt); + return nClipPath(mRenderer, path.mNativePath, op.nativeInt); } + private static native boolean nClipPath(int renderer, int path, int op); + @Override public boolean clipRect(float left, float top, float right, float bottom) { return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); @@ -465,8 +451,8 @@ class GLES20Canvas extends HardwareCanvas { return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } - private static native boolean nClipRect(int renderer, int left, int top, int right, int bottom, - int op); + private static native boolean nClipRect(int renderer, int left, int top, + int right, int bottom, int op); @Override public boolean clipRect(Rect rect) { @@ -492,20 +478,16 @@ class GLES20Canvas extends HardwareCanvas { @Override public boolean clipRegion(Region region) { - // TODO: Implement - region.getBounds(mClipBounds); - return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top, - mClipBounds.right, mClipBounds.bottom, Region.Op.INTERSECT.nativeInt); + return nClipRegion(mRenderer, region.mNativeRegion, Region.Op.INTERSECT.nativeInt); } @Override public boolean clipRegion(Region region, Region.Op op) { - // TODO: Implement - region.getBounds(mClipBounds); - return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top, - mClipBounds.right, mClipBounds.bottom, op.nativeInt); + return nClipRegion(mRenderer, region.mNativeRegion, op.nativeInt); } + private static native boolean nClipRegion(int renderer, int region, int op); + @Override public boolean getClipBounds(Rect bounds) { return nGetClipBounds(mRenderer, bounds); @@ -901,9 +883,9 @@ class GLES20Canvas extends HardwareCanvas { final int count = (meshWidth + 1) * (meshHeight + 1); checkRange(verts.length, vertOffset, count * 2); - // TODO: Colors are ignored for now - colors = null; - colorOffset = 0; + if (colors != null) { + checkRange(colors.length, colorOffset, count); + } int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; try { @@ -955,6 +937,8 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawLines(float[] pts, int offset, int count, Paint paint) { + if (count < 4) return; + if ((offset | count) < 0 || offset + count > pts.length) { throw new IllegalArgumentException("The lines array must contain 4 elements per line."); } @@ -1013,6 +997,17 @@ class GLES20Canvas extends HardwareCanvas { private static native void nDrawPath(int renderer, int path, int paint); private static native void nDrawRects(int renderer, int region, int paint); + void drawRects(float[] rects, int count, Paint paint) { + int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER); + try { + nDrawRects(mRenderer, rects, count, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } + } + + private static native void nDrawRects(int renderer, float[] rects, int count, int paint); + @Override public void drawPicture(Picture picture) { if (picture.createdFromStream) { @@ -1067,6 +1062,8 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPoints(float[] pts, int offset, int count, Paint paint) { + if (count < 2) return; + int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER); try { nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint); @@ -1165,14 +1162,14 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { - nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint); + nDrawText(mRenderer, text, index, count, x, y, paint.mNativePaint); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawText(int renderer, char[] text, int index, int count, - float x, float y, int bidiFlags, int paint); + float x, float y, int paint); @Override public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) { @@ -1180,16 +1177,14 @@ class GLES20Canvas extends HardwareCanvas { try { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { - nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags, - paint.mNativePaint); + nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mNativePaint); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawText(this, start, end, x, y, paint); } else { char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); - nDrawText(mRenderer, buf, 0, end - start, x, y, - paint.mBidiFlags, paint.mNativePaint); + nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mNativePaint); TemporaryBuffer.recycle(buf); } } finally { @@ -1205,21 +1200,20 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { - nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint); + nDrawText(mRenderer, text, start, end, x, y, paint.mNativePaint); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawText(int renderer, String text, int start, int end, - float x, float y, int bidiFlags, int paint); + float x, float y, int paint); @Override public void drawText(String text, float x, float y, Paint paint) { int modifiers = setupModifiers(paint); try { - nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags, - paint.mNativePaint); + nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mNativePaint); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } @@ -1235,14 +1229,14 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mNativePaint); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawTextOnPath(int renderer, char[] text, int index, int count, - int path, float hOffset, float vOffset, int bidiFlags, int nativePaint); + int path, float hOffset, float vOffset, int nativePaint); @Override public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { @@ -1251,28 +1245,25 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mNativePaint); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawTextOnPath(int renderer, String text, int start, int end, - int path, float hOffset, float vOffset, int bidiFlags, int nativePaint); + int path, float hOffset, float vOffset, int nativePaint); @Override public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, - float x, float y, int dir, Paint paint) { + float x, float y, Paint paint) { if ((index | count | text.length - index - count) < 0) { throw new IndexOutOfBoundsException(); } - if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) { - throw new IllegalArgumentException("Unknown direction: " + dir); - } int modifiers = setupModifiers(paint); try { - nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir, + nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, paint.mNativePaint); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); @@ -1280,32 +1271,31 @@ class GLES20Canvas extends HardwareCanvas { } private static native void nDrawTextRun(int renderer, char[] text, int index, int count, - int contextIndex, int contextCount, float x, float y, int dir, int nativePaint); + int contextIndex, int contextCount, float x, float y, int nativePaint); @Override public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, - float x, float y, int dir, Paint paint) { + float x, float y, Paint paint) { if ((start | end | end - start | text.length() - end) < 0) { throw new IndexOutOfBoundsException(); } int modifiers = setupModifiers(paint); try { - int flags = dir == 0 ? 0 : 1; if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawTextRun(mRenderer, text.toString(), start, end, contextStart, - contextEnd, x, y, flags, paint.mNativePaint); + contextEnd, x, y, paint.mNativePaint); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawTextRun(this, start, end, - contextStart, contextEnd, x, y, flags, paint); + contextStart, contextEnd, x, y, paint); } else { int contextLen = contextEnd - contextStart; int len = end - start; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen, - x, y, flags, paint.mNativePaint); + x, y, paint.mNativePaint); TemporaryBuffer.recycle(buf); } } finally { @@ -1314,7 +1304,7 @@ class GLES20Canvas extends HardwareCanvas { } private static native void nDrawTextRun(int renderer, String text, int start, int end, - int contextStart, int contextEnd, float x, float y, int flags, int nativePaint); + int contextStart, int contextEnd, float x, float y, int nativePaint); @Override public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index e9bd0c4..9c5cdb2 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -58,7 +58,7 @@ class GLES20DisplayList extends DisplayList { } @Override - public HardwareCanvas start() { + public HardwareCanvas start(int width, int height) { if (mCanvas != null) { throw new IllegalStateException("Recording has already started"); } @@ -66,24 +66,25 @@ class GLES20DisplayList extends DisplayList { mValid = false; mCanvas = GLES20RecordingCanvas.obtain(this); mCanvas.start(); + + mCanvas.setViewport(width, height); + // The dirty rect should always be null for a display list + mCanvas.onPreDraw(null); + return mCanvas; } - @Override - public void invalidate() { + public void clear() { + clearDirty(); + if (mCanvas != null) { mCanvas.recycle(); mCanvas = null; } mValid = false; - } - @Override - public void clear() { - if (!mValid) { - mBitmaps.clear(); - mChildDisplayLists.clear(); - } + mBitmaps.clear(); + mChildDisplayLists.clear(); } @Override @@ -101,11 +102,12 @@ class GLES20DisplayList extends DisplayList { @Override public void end() { if (mCanvas != null) { + mCanvas.onPostDraw(); if (mFinalizer != null) { mCanvas.end(mFinalizer.mNativeDisplayList); } else { mFinalizer = new DisplayListFinalizer(mCanvas.end(0)); - GLES20Canvas.setDisplayListName(mFinalizer.mNativeDisplayList, mName); + nSetDisplayListName(mFinalizer.mNativeDisplayList, mName); } mCanvas.recycle(); mCanvas = null; @@ -116,9 +118,13 @@ class GLES20DisplayList extends DisplayList { @Override public int getSize() { if (mFinalizer == null) return 0; - return GLES20Canvas.getDisplayListSize(mFinalizer.mNativeDisplayList); + return nGetDisplayListSize(mFinalizer.mNativeDisplayList); } + private static native void nDestroyDisplayList(int displayList); + private static native int nGetDisplayListSize(int displayList); + private static native void nSetDisplayListName(int displayList, String name); + /////////////////////////////////////////////////////////////////////////// // Native View Properties /////////////////////////////////////////////////////////////////////////// @@ -138,13 +144,21 @@ class GLES20DisplayList extends DisplayList { } @Override - public void setStaticMatrix(Matrix matrix) { + public void setMatrix(Matrix matrix) { if (hasNativeDisplayList()) { nSetStaticMatrix(mFinalizer.mNativeDisplayList, matrix.native_instance); } } @Override + public Matrix getMatrix(Matrix matrix) { + if (hasNativeDisplayList()) { + nGetMatrix(mFinalizer.mNativeDisplayList, matrix.native_instance); + } + return matrix; + } + + @Override public void setAnimationMatrix(Matrix matrix) { if (hasNativeDisplayList()) { nSetAnimationMatrix(mFinalizer.mNativeDisplayList, @@ -160,6 +174,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getAlpha() { + if (hasNativeDisplayList()) { + return nGetAlpha(mFinalizer.mNativeDisplayList); + } + return 1.0f; + } + + @Override public void setHasOverlappingRendering(boolean hasOverlappingRendering) { if (hasNativeDisplayList()) { nSetHasOverlappingRendering(mFinalizer.mNativeDisplayList, hasOverlappingRendering); @@ -167,6 +189,15 @@ class GLES20DisplayList extends DisplayList { } @Override + public boolean hasOverlappingRendering() { + //noinspection SimplifiableIfStatement + if (hasNativeDisplayList()) { + return nHasOverlappingRendering(mFinalizer.mNativeDisplayList); + } + return true; + } + + @Override public void setTranslationX(float translationX) { if (hasNativeDisplayList()) { nSetTranslationX(mFinalizer.mNativeDisplayList, translationX); @@ -174,6 +205,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getTranslationX() { + if (hasNativeDisplayList()) { + return nGetTranslationX(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setTranslationY(float translationY) { if (hasNativeDisplayList()) { nSetTranslationY(mFinalizer.mNativeDisplayList, translationY); @@ -181,6 +220,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getTranslationY() { + if (hasNativeDisplayList()) { + return nGetTranslationY(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setRotation(float rotation) { if (hasNativeDisplayList()) { nSetRotation(mFinalizer.mNativeDisplayList, rotation); @@ -188,6 +235,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getRotation() { + if (hasNativeDisplayList()) { + return nGetRotation(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setRotationX(float rotationX) { if (hasNativeDisplayList()) { nSetRotationX(mFinalizer.mNativeDisplayList, rotationX); @@ -195,6 +250,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getRotationX() { + if (hasNativeDisplayList()) { + return nGetRotationX(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setRotationY(float rotationY) { if (hasNativeDisplayList()) { nSetRotationY(mFinalizer.mNativeDisplayList, rotationY); @@ -202,6 +265,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getRotationY() { + if (hasNativeDisplayList()) { + return nGetRotationY(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setScaleX(float scaleX) { if (hasNativeDisplayList()) { nSetScaleX(mFinalizer.mNativeDisplayList, scaleX); @@ -209,6 +280,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getScaleX() { + if (hasNativeDisplayList()) { + return nGetScaleX(mFinalizer.mNativeDisplayList); + } + return 1.0f; + } + + @Override public void setScaleY(float scaleY) { if (hasNativeDisplayList()) { nSetScaleY(mFinalizer.mNativeDisplayList, scaleY); @@ -216,6 +295,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getScaleY() { + if (hasNativeDisplayList()) { + return nGetScaleY(mFinalizer.mNativeDisplayList); + } + return 1.0f; + } + + @Override public void setTransformationInfo(float alpha, float translationX, float translationY, float rotation, float rotationX, float rotationY, float scaleX, float scaleY) { if (hasNativeDisplayList()) { @@ -232,6 +319,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getPivotX() { + if (hasNativeDisplayList()) { + return nGetPivotX(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setPivotY(float pivotY) { if (hasNativeDisplayList()) { nSetPivotY(mFinalizer.mNativeDisplayList, pivotY); @@ -239,6 +334,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getPivotY() { + if (hasNativeDisplayList()) { + return nGetPivotY(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setCameraDistance(float distance) { if (hasNativeDisplayList()) { nSetCameraDistance(mFinalizer.mNativeDisplayList, distance); @@ -246,6 +349,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getCameraDistance() { + if (hasNativeDisplayList()) { + return nGetCameraDistance(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setLeft(int left) { if (hasNativeDisplayList()) { nSetLeft(mFinalizer.mNativeDisplayList, left); @@ -253,6 +364,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getLeft() { + if (hasNativeDisplayList()) { + return nGetLeft(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setTop(int top) { if (hasNativeDisplayList()) { nSetTop(mFinalizer.mNativeDisplayList, top); @@ -260,6 +379,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getTop() { + if (hasNativeDisplayList()) { + return nGetTop(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setRight(int right) { if (hasNativeDisplayList()) { nSetRight(mFinalizer.mNativeDisplayList, right); @@ -267,6 +394,14 @@ class GLES20DisplayList extends DisplayList { } @Override + public float getRight() { + if (hasNativeDisplayList()) { + return nGetRight(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setBottom(int bottom) { if (hasNativeDisplayList()) { nSetBottom(mFinalizer.mNativeDisplayList, bottom); @@ -274,10 +409,11 @@ class GLES20DisplayList extends DisplayList { } @Override - public void setLeftTop(int left, int top) { + public float getBottom() { if (hasNativeDisplayList()) { - nSetLeftTop(mFinalizer.mNativeDisplayList, left, top); + return nGetBottom(mFinalizer.mNativeDisplayList); } + return 0.0f; } @Override @@ -288,25 +424,24 @@ class GLES20DisplayList extends DisplayList { } @Override - public void offsetLeftRight(int offset) { + public void offsetLeftAndRight(float offset) { if (hasNativeDisplayList()) { - nOffsetLeftRight(mFinalizer.mNativeDisplayList, offset); + nOffsetLeftAndRight(mFinalizer.mNativeDisplayList, offset); } } @Override - public void offsetTopBottom(int offset) { + public void offsetTopAndBottom(float offset) { if (hasNativeDisplayList()) { - nOffsetTopBottom(mFinalizer.mNativeDisplayList, offset); + nOffsetTopAndBottom(mFinalizer.mNativeDisplayList, offset); } } private static native void nReset(int displayList); - private static native void nOffsetTopBottom(int displayList, int offset); - private static native void nOffsetLeftRight(int displayList, int offset); + private static native void nOffsetTopAndBottom(int displayList, float offset); + private static native void nOffsetLeftAndRight(int displayList, float offset); private static native void nSetLeftTopRightBottom(int displayList, int left, int top, int right, int bottom); - private static native void nSetLeftTop(int displayList, int left, int top); private static native void nSetBottom(int displayList, int bottom); private static native void nSetRight(int displayList, int right); private static native void nSetTop(int displayList, int top); @@ -332,6 +467,23 @@ class GLES20DisplayList extends DisplayList { private static native void nSetStaticMatrix(int displayList, int nativeMatrix); private static native void nSetAnimationMatrix(int displayList, int animationMatrix); + private static native boolean nHasOverlappingRendering(int displayList); + private static native void nGetMatrix(int displayList, int matrix); + private static native float nGetAlpha(int displayList); + private static native float nGetLeft(int displayList); + private static native float nGetTop(int displayList); + private static native float nGetRight(int displayList); + private static native float nGetBottom(int displayList); + private static native float nGetCameraDistance(int displayList); + private static native float nGetScaleX(int displayList); + private static native float nGetScaleY(int displayList); + private static native float nGetTranslationX(int displayList); + private static native float nGetTranslationY(int displayList); + private static native float nGetRotation(int displayList); + private static native float nGetRotationX(int displayList); + private static native float nGetRotationY(int displayList); + private static native float nGetPivotX(int displayList); + private static native float nGetPivotY(int displayList); /////////////////////////////////////////////////////////////////////////// // Finalization @@ -347,7 +499,7 @@ class GLES20DisplayList extends DisplayList { @Override protected void finalize() throws Throwable { try { - GLES20Canvas.destroyDisplayList(mNativeDisplayList); + nDestroyDisplayList(mNativeDisplayList); } finally { super.finalize(); } diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java index 812fb97..7ee628b 100644 --- a/core/java/android/view/GLES20Layer.java +++ b/core/java/android/view/GLES20Layer.java @@ -53,12 +53,12 @@ abstract class GLES20Layer extends HardwareLayer { } @Override - boolean copyInto(Bitmap bitmap) { + public boolean copyInto(Bitmap bitmap) { return GLES20Canvas.nCopyLayer(mLayer, bitmap.mNativeBitmap); } @Override - void destroy() { + public void destroy() { if (mFinalizer != null) { mFinalizer.destroy(); mFinalizer = null; diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java index d2df45a..947cf44 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -24,10 +24,7 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; -import android.util.Pool; -import android.util.Poolable; -import android.util.PoolableManager; -import android.util.Pools; +import android.util.Pools.SynchronizedPool; /** * An implementation of a GL canvas that records drawing operations. @@ -35,35 +32,25 @@ import android.util.Pools; * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while * the DisplayList is still holding a native reference to the memory. */ -class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20RecordingCanvas> { +class GLES20RecordingCanvas extends GLES20Canvas { // The recording canvas pool should be large enough to handle a deeply nested // view hierarchy because display lists are generated recursively. private static final int POOL_LIMIT = 25; - private static final Pool<GLES20RecordingCanvas> sPool = Pools.synchronizedPool( - Pools.finitePool(new PoolableManager<GLES20RecordingCanvas>() { - public GLES20RecordingCanvas newInstance() { - return new GLES20RecordingCanvas(); - } - @Override - public void onAcquired(GLES20RecordingCanvas element) { - } - @Override - public void onReleased(GLES20RecordingCanvas element) { - } - }, POOL_LIMIT)); - - private GLES20RecordingCanvas mNextPoolable; - private boolean mIsPooled; + private static final SynchronizedPool<GLES20RecordingCanvas> sPool = + new SynchronizedPool<GLES20RecordingCanvas>(POOL_LIMIT); private GLES20DisplayList mDisplayList; private GLES20RecordingCanvas() { - super(true /*record*/, true /*translucent*/); + super(true, true); } static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) { GLES20RecordingCanvas canvas = sPool.acquire(); + if (canvas == null) { + canvas = new GLES20RecordingCanvas(); + } canvas.mDisplayList = displayList; return canvas; } @@ -280,15 +267,15 @@ class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20Recor @Override public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, - float x, float y, int dir, Paint paint) { - super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, dir, paint); + float x, float y, Paint paint) { + super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, paint); recordShaderBitmap(paint); } @Override public void drawTextRun(CharSequence text, int start, int end, int contextStart, - int contextEnd, float x, float y, int dir, Paint paint) { - super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, dir, paint); + int contextEnd, float x, float y, Paint paint) { + super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, paint); recordShaderBitmap(paint); } @@ -300,24 +287,4 @@ class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20Recor colorOffset, indices, indexOffset, indexCount, paint); recordShaderBitmap(paint); } - - @Override - public GLES20RecordingCanvas getNextPoolable() { - return mNextPoolable; - } - - @Override - public void setNextPoolable(GLES20RecordingCanvas element) { - mNextPoolable = element; - } - - @Override - public boolean isPooled() { - return mIsPooled; - } - - @Override - public void setPooled(boolean isPooled) { - mIsPooled = isPooled; - } } diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java index 44d4719..086e78c 100644 --- a/core/java/android/view/GLES20RenderLayer.java +++ b/core/java/android/view/GLES20RenderLayer.java @@ -92,6 +92,10 @@ class GLES20RenderLayer extends GLES20Layer { if (currentCanvas instanceof GLES20Canvas) { ((GLES20Canvas) currentCanvas).resume(); } + HardwareCanvas canvas = getCanvas(); + if (canvas != null) { + canvas.onPostDraw(); + } } @Override @@ -99,7 +103,10 @@ class GLES20RenderLayer extends GLES20Layer { if (currentCanvas instanceof GLES20Canvas) { ((GLES20Canvas) currentCanvas).interrupt(); } - return getCanvas(); + HardwareCanvas canvas = getCanvas(); + canvas.setViewport(mWidth, mHeight); + canvas.onPreDraw(null); + return canvas; } /** diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 9ddb32e..28c1058 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -213,6 +213,7 @@ public class GestureDetector { private OnDoubleTapListener mDoubleTapListener; private boolean mStillDown; + private boolean mDeferConfirmSingleTap; private boolean mInLongPress; private boolean mAlwaysInTapRegion; private boolean mAlwaysInBiggerTapRegion; @@ -267,8 +268,12 @@ public class GestureDetector { case TAP: // If the user's finger is still down, do not count it as a tap - if (mDoubleTapListener != null && !mStillDown) { - mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); + if (mDoubleTapListener != null) { + if (!mStillDown) { + mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); + } else { + mDeferConfirmSingleTap = true; + } } break; @@ -533,6 +538,7 @@ public class GestureDetector { mAlwaysInBiggerTapRegion = true; mStillDown = true; mInLongPress = false; + mDeferConfirmSingleTap = false; if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); @@ -586,6 +592,9 @@ public class GestureDetector { mInLongPress = false; } else if (mAlwaysInTapRegion) { handled = mListener.onSingleTapUp(ev); + if (mDeferConfirmSingleTap && mDoubleTapListener != null) { + mDoubleTapListener.onSingleTapConfirmed(ev); + } } else { // A fling must travel the minimum tap distance @@ -612,6 +621,7 @@ public class GestureDetector { mVelocityTracker = null; } mIsDoubleTapping = false; + mDeferConfirmSingleTap = false; mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); break; @@ -637,6 +647,7 @@ public class GestureDetector { mStillDown = false; mAlwaysInTapRegion = false; mAlwaysInBiggerTapRegion = false; + mDeferConfirmSingleTap = false; if (mInLongPress) { mInLongPress = false; } @@ -649,6 +660,7 @@ public class GestureDetector { mIsDoubleTapping = false; mAlwaysInTapRegion = false; mAlwaysInBiggerTapRegion = false; + mDeferConfirmSingleTap = false; if (mInLongPress) { mInLongPress = false; } @@ -671,6 +683,7 @@ public class GestureDetector { private void dispatchLongPress() { mHandler.removeMessages(TAP); + mDeferConfirmSingleTap = false; mInLongPress = true; mListener.onLongPress(mCurrentDownEvent); } diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index eeae3ed..0dfed69 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -23,10 +23,12 @@ import android.graphics.Rect; /** * Hardware accelerated canvas. - * - * @hide + * + * @hide */ public abstract class HardwareCanvas extends Canvas { + private String mName; + @Override public boolean isHardwareAccelerated() { return true; @@ -36,33 +38,76 @@ public abstract class HardwareCanvas extends Canvas { public void setBitmap(Bitmap bitmap) { throw new UnsupportedOperationException(); } - + + /** + * Specifies the name of this canvas. Naming the canvas is entirely + * optional but can be useful for debugging purposes. + * + * @param name The name of the canvas, can be null + * + * @see #getName() + * + * @hide + */ + public void setName(String name) { + mName = name; + } + + /** + * Returns the name of this canvas. + * + * @return The name of the canvas or null + * + * @see #setName(String) + * + * @hide + */ + public String getName() { + return mName; + } + /** * Invoked before any drawing operation is performed in this canvas. * * @param dirty The dirty rectangle to update, can be null. * @return {@link DisplayList#STATUS_DREW} if anything was drawn (such as a call to clear - * the canvas). + * the canvas). + * + * @hide */ public abstract int onPreDraw(Rect dirty); /** * Invoked after all drawing operation have been performed. + * + * @hide */ public abstract void onPostDraw(); /** + * Draws the specified display list onto this canvas. The display list can only + * be drawn if {@link android.view.DisplayList#isValid()} returns true. + * + * @param displayList The display list to replay. + */ + public void drawDisplayList(DisplayList displayList) { + drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN); + } + + /** * Draws the specified display list onto this canvas. * * @param displayList The display list to replay. * @param dirty The dirty region to redraw in the next pass, matters only - * if this method returns true, can be null. + * if this method returns {@link DisplayList#STATUS_DRAW}, can be null. * @param flags Optional flags about drawing, see {@link DisplayList} for * the possible flags. * * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW}, or * {@link DisplayList#STATUS_INVOKE}, or'd with {@link DisplayList#STATUS_DREW} * if anything was drawn. + * + * @hide */ public abstract int drawDisplayList(DisplayList displayList, Rect dirty, int flags); @@ -71,6 +116,8 @@ public abstract class HardwareCanvas extends Canvas { * tools to output display lists for selected nodes to the log. * * @param displayList The display list to be logged. + * + * @hide */ abstract void outputDisplayList(DisplayList displayList); @@ -81,6 +128,8 @@ public abstract class HardwareCanvas extends Canvas { * @param x The left coordinate of the layer * @param y The top coordinate of the layer * @param paint The paint used to draw the layer + * + * @hide */ abstract void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint); @@ -93,6 +142,8 @@ public abstract class HardwareCanvas extends Canvas { * * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or * {@link DisplayList#STATUS_INVOKE} + * + * @hide */ public int callDrawGLFunction(int drawGLFunction) { // Noop - this is done in the display list recorder subclass @@ -106,6 +157,8 @@ public abstract class HardwareCanvas extends Canvas { * * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or * {@link DisplayList#STATUS_INVOKE} + * + * @hide */ public int invokeFunctors(Rect dirty) { return DisplayList.STATUS_DONE; @@ -118,7 +171,9 @@ public abstract class HardwareCanvas extends Canvas { * * @see #invokeFunctors(android.graphics.Rect) * @see #callDrawGLFunction(int) - * @see #detachFunctor(int) + * @see #detachFunctor(int) + * + * @hide */ abstract void detachFunctor(int functor); @@ -129,7 +184,9 @@ public abstract class HardwareCanvas extends Canvas { * * @see #invokeFunctors(android.graphics.Rect) * @see #callDrawGLFunction(int) - * @see #detachFunctor(int) + * @see #detachFunctor(int) + * + * @hide */ abstract void attachFunctor(int functor); @@ -139,13 +196,17 @@ public abstract class HardwareCanvas extends Canvas { * @param layer The layer to update * * @see #clearLayerUpdates() + * + * @hide */ abstract void pushLayerUpdate(HardwareLayer layer); /** * Removes all enqueued layer updates. * - * @see #pushLayerUpdate(HardwareLayer) + * @see #pushLayerUpdate(HardwareLayer) + * + * @hide */ abstract void clearLayerUpdates(); } diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index d3bc35a..18b838b 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -24,7 +24,7 @@ import android.graphics.Rect; /** * A hardware layer can be used to render graphics operations into a hardware - * friendly buffer. For instance, with an OpenGL backend, a hardware layer + * friendly buffer. For instance, with an OpenGL backend a hardware layer * would use a Frame Buffer Object (FBO.) The hardware layer can be used as * a drawing cache when a complex set of graphics operations needs to be * drawn several times. @@ -68,7 +68,7 @@ abstract class HardwareLayer { * @param paint The paint used when the layer is drawn into the destination canvas. * @see View#setLayerPaint(android.graphics.Paint) */ - void setLayerPaint(Paint paint) {} + void setLayerPaint(Paint paint) { } /** * Returns the minimum width of the layer. @@ -144,6 +144,9 @@ abstract class HardwareLayer { * this layer. * * @return A hardware canvas, or null if a canvas cannot be created + * + * @see #start(android.graphics.Canvas) + * @see #end(android.graphics.Canvas) */ abstract HardwareCanvas getCanvas(); @@ -154,12 +157,14 @@ abstract class HardwareLayer { /** * This must be invoked before drawing onto this layer. + * * @param currentCanvas */ abstract HardwareCanvas start(Canvas currentCanvas); - + /** * This must be invoked after drawing onto this layer. + * * @param currentCanvas */ abstract void end(Canvas currentCanvas); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 5b7a5af..7929112 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ * limitations under the License. */ - package android.view; import android.content.ComponentCallbacks2; @@ -29,6 +28,7 @@ import android.os.Looper; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.util.DisplayMetrics; import android.util.Log; import com.google.android.gles_jni.EGLImpl; @@ -42,13 +42,14 @@ import javax.microedition.khronos.opengles.GL; import java.io.File; import java.io.PrintWriter; +import java.util.Arrays; import java.util.concurrent.locks.ReentrantLock; import static javax.microedition.khronos.egl.EGL10.*; /** - * Interface for rendering a ViewAncestor using hardware acceleration. - * + * Interface for rendering a view hierarchy using hardware acceleration. + * * @hide */ public abstract class HardwareRenderer { @@ -62,9 +63,9 @@ public abstract class HardwareRenderer { /** * Turn on to only refresh the parts of the screen that need updating. * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} - * must also have the value "true". + * must also have the value "true". */ - public static final boolean RENDER_DIRTY_REGIONS = true; + static final boolean RENDER_DIRTY_REGIONS = true; /** * System property used to enable or disable dirty regions invalidation. @@ -76,16 +77,6 @@ public abstract class HardwareRenderer { * "false", to disable partial invalidates */ static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions"; - - /** - * System property used to enable or disable vsync. - * The default value of this property is assumed to be false. - * - * Possible values: - * "true", to disable vsync - * "false", to enable vsync - */ - static final String DISABLE_VSYNC_PROPERTY = "debug.hwui.disable_vsync"; /** * System property used to enable or disable hardware rendering profiling. @@ -97,13 +88,34 @@ public abstract class HardwareRenderer { * * Possible values: * "true", to enable profiling + * "visual_bars", to enable profiling and visualize the results on screen + * "visual_lines", to enable profiling and visualize the results on screen * "false", to disable profiling - * + * + * @see #PROFILE_PROPERTY_VISUALIZE_BARS + * @see #PROFILE_PROPERTY_VISUALIZE_LINES + * * @hide */ public static final String PROFILE_PROPERTY = "debug.hwui.profile"; /** + * Value for {@link #PROFILE_PROPERTY}. When the property is set to this + * value, profiling data will be visualized on screen as a bar chart. + * + * @hide + */ + public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars"; + + /** + * Value for {@link #PROFILE_PROPERTY}. When the property is set to this + * value, profiling data will be visualized on screen as a line chart. + * + * @hide + */ + public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines"; + + /** * System property used to specify the number of frames to be used * when doing hardware rendering profiling. * The default value of this property is #PROFILE_MAX_FRAMES. @@ -323,10 +335,26 @@ public abstract class HardwareRenderer { abstract long getFrameCount(); /** + * Loads system properties used by the renderer. This method is invoked + * whenever system properties are modified. Implementations can use this + * to trigger live updates of the renderer based on properties. + * + * @param surface The surface to update with the new properties. + * Can be null. + * + * @return True if a property has changed. + */ + abstract boolean loadSystemProperties(Surface surface); + + private static native boolean nLoadProperties(); + + /** * Sets the directory to use as a persistent storage for hardware rendering * resources. * * @param cacheDir A directory the current process can write to + * + * @hide */ public static void setupDiskCache(File cacheDir) { nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); @@ -338,7 +366,7 @@ public abstract class HardwareRenderer { * Notifies EGL that the frame is about to be rendered. * @param size */ - private static void beginFrame(int[] size) { + static void beginFrame(int[] size) { nBeginFrame(size); } @@ -373,15 +401,6 @@ public abstract class HardwareRenderer { private static native boolean nIsBackBufferPreserved(); /** - * Disables v-sync. For performance testing only. - */ - static void disableVsync() { - nDisableVsync(); - } - - private static native void nDisableVsync(); - - /** * Indicates that the specified hardware layer needs to be updated * as soon as possible. * @@ -426,10 +445,11 @@ public abstract class HardwareRenderer { * Creates a new display list that can be used to record batches of * drawing operations. * - * @param name The name of the display list, used for debugging purpose. - * May be null + * @param name The name of the display list, used for debugging purpose. May be null. * * @return A new display list. + * + * @hide */ public abstract DisplayList createDisplayList(String name); @@ -457,7 +477,6 @@ public abstract class HardwareRenderer { /** * Creates a new {@link SurfaceTexture} that can be used to render into the * specified hardware layer. - * * * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} * @@ -526,6 +545,13 @@ public abstract class HardwareRenderer { } /** + * Optional, sets the name of the renderer. Useful for debugging purposes. + * + * @param name The name of this renderer, can be null + */ + abstract void setName(String name); + + /** * Creates a hardware renderer using OpenGL. * * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) @@ -612,6 +638,100 @@ public abstract class HardwareRenderer { mRequested = requested; } + /** + * Describes a series of frames that should be drawn on screen as a graph. + * Each frame is composed of 1 or more elements. + */ + abstract class GraphDataProvider { + /** + * Draws the graph as bars. Frame elements are stacked on top of + * each other. + */ + public static final int GRAPH_TYPE_BARS = 0; + /** + * Draws the graph as lines. The number of series drawn corresponds + * to the number of elements. + */ + public static final int GRAPH_TYPE_LINES = 1; + + /** + * Returns the type of graph to render. + * + * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES} + */ + abstract int getGraphType(); + + /** + * This method is invoked before the graph is drawn. This method + * can be used to compute sizes, etc. + * + * @param metrics The display metrics + */ + abstract void prepare(DisplayMetrics metrics); + + /** + * @return The size in pixels of a vertical unit. + */ + abstract int getVerticalUnitSize(); + + /** + * @return The size in pixels of a horizontal unit. + */ + abstract int getHorizontalUnitSize(); + + /** + * @return The size in pixels of the margin between horizontal units. + */ + abstract int getHorizontaUnitMargin(); + + /** + * An optional threshold value. + * + * @return A value >= 0 to draw the threshold, a negative value + * to ignore it. + */ + abstract float getThreshold(); + + /** + * The data to draw in the graph. The number of elements in the + * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}. + * If a value is negative the following values will be ignored. + */ + abstract float[] getData(); + + /** + * Returns the number of frames to render in the graph. + */ + abstract int getFrameCount(); + + /** + * Returns the number of elements in each frame. This directly affects + * the number of series drawn in the graph. + */ + abstract int getElementCount(); + + /** + * Returns the current frame, if any. If the returned value is negative + * the current frame is ignored. + */ + abstract int getCurrentFrame(); + + /** + * Prepares the paint to draw the specified element (or series.) + */ + abstract void setupGraphPaint(Paint paint, int elementIndex); + + /** + * Prepares the paint to draw the threshold. + */ + abstract void setupThresholdPaint(Paint paint); + + /** + * Prepares the paint to draw the current frame indicator. + */ + abstract void setupCurrentFramePaint(Paint paint); + } + @SuppressWarnings({"deprecation"}) static abstract class GlRenderer extends HardwareRenderer { static final int SURFACE_STATE_ERROR = 0; @@ -620,6 +740,14 @@ public abstract class HardwareRenderer { static final int FUNCTOR_PROCESS_DELAY = 4; + private static final int PROFILE_DRAW_MARGIN = 0; + private static final int PROFILE_DRAW_WIDTH = 3; + private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 }; + private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d; + private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d; + private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2; + private static final int PROFILE_DRAW_DP_PER_MS = 7; + static EGL10 sEgl; static EGLDisplay sEglDisplay; static EGLConfig sEglConfig; @@ -637,6 +765,8 @@ public abstract class HardwareRenderer { GL mGl; HardwareCanvas mCanvas; + String mName; + long mFrameCount; Paint mDebugPaint; @@ -652,15 +782,18 @@ public abstract class HardwareRenderer { boolean mDirtyRegionsEnabled; boolean mUpdateDirtyRegions; - final boolean mVsyncDisabled; - - final boolean mProfileEnabled; - final float[] mProfileData; - final ReentrantLock mProfileLock; + boolean mProfileEnabled; + int mProfileVisualizerType = -1; + float[] mProfileData; + ReentrantLock mProfileLock; int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; - - final boolean mDebugDirtyRegions; - final boolean mShowOverdraw; + + GraphDataProvider mDebugDataProvider; + float[][] mProfileShapes; + Paint mProfilePaint; + + boolean mDebugDirtyRegions; + boolean mShowOverdraw; final int mGlVersion; final boolean mTranslucent; @@ -675,44 +808,90 @@ public abstract class HardwareRenderer { GlRenderer(int glVersion, boolean translucent) { mGlVersion = glVersion; mTranslucent = translucent; - - String property; - property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); - mVsyncDisabled = "true".equalsIgnoreCase(property); - if (mVsyncDisabled) { - Log.d(LOG_TAG, "Disabling v-sync"); + loadSystemProperties(null); + } + + private static final String[] VISUALIZERS = { + PROFILE_PROPERTY_VISUALIZE_BARS, + PROFILE_PROPERTY_VISUALIZE_LINES + }; + + @Override + boolean loadSystemProperties(Surface surface) { + boolean value; + boolean changed = false; + + String profiling = SystemProperties.get(PROFILE_PROPERTY); + int graphType = Arrays.binarySearch(VISUALIZERS, profiling); + value = graphType >= 0; + + if (graphType != mProfileVisualizerType) { + changed = true; + mProfileVisualizerType = graphType; + + mProfileShapes = null; + mProfilePaint = null; + + if (value) { + mDebugDataProvider = new DrawPerformanceDataProvider(graphType); + } else { + mDebugDataProvider = null; + } } - property = SystemProperties.get(PROFILE_PROPERTY, "false"); - mProfileEnabled = "true".equalsIgnoreCase(property); - if (mProfileEnabled) { - Log.d(LOG_TAG, "Profiling hardware renderer"); + // If on-screen profiling is not enabled, we need to check whether + // console profiling only is enabled + if (!value) { + value = Boolean.parseBoolean(profiling); } - if (mProfileEnabled) { - property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY, - Integer.toString(PROFILE_MAX_FRAMES)); - int maxProfileFrames = Integer.valueOf(property); - mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT]; - for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { - mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; + if (value != mProfileEnabled) { + changed = true; + mProfileEnabled = value; + + if (mProfileEnabled) { + Log.d(LOG_TAG, "Profiling hardware renderer"); + + int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY, + PROFILE_MAX_FRAMES); + mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT]; + for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { + mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; + } + + mProfileLock = new ReentrantLock(); + } else { + mProfileData = null; + mProfileLock = null; + mProfileVisualizerType = -1; } - mProfileLock = new ReentrantLock(); - } else { - mProfileData = null; - mProfileLock = null; + mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; } - property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false"); - mDebugDirtyRegions = "true".equalsIgnoreCase(property); - if (mDebugDirtyRegions) { - Log.d(LOG_TAG, "Debugging dirty regions"); + value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false); + if (value != mDebugDirtyRegions) { + changed = true; + mDebugDirtyRegions = value; + + if (mDebugDirtyRegions) { + Log.d(LOG_TAG, "Debugging dirty regions"); + } } - mShowOverdraw = SystemProperties.getBoolean( + value = SystemProperties.getBoolean( HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false); + if (value != mShowOverdraw) { + changed = true; + mShowOverdraw = value; + } + + if (nLoadProperties()) { + changed = true; + } + + return changed; } @Override @@ -795,6 +974,7 @@ public abstract class HardwareRenderer { } else { if (mCanvas == null) { mCanvas = createCanvas(); + mCanvas.setName(mName); } if (mCanvas != null) { setEnabled(true); @@ -842,19 +1022,7 @@ public abstract class HardwareRenderer { checkEglErrorsForced(); - sEglConfig = chooseEglConfig(); - if (sEglConfig == null) { - // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without - if (sDirtyRegions) { - sDirtyRegions = false; - sEglConfig = chooseEglConfig(); - if (sEglConfig == null) { - throw new RuntimeException("eglConfig not initialized"); - } - } else { - throw new RuntimeException("eglConfig not initialized"); - } - } + sEglConfig = loadEglConfig(); } } @@ -868,6 +1036,23 @@ public abstract class HardwareRenderer { } } + private EGLConfig loadEglConfig() { + EGLConfig eglConfig = chooseEglConfig(); + if (eglConfig == null) { + // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without + if (sDirtyRegions) { + sDirtyRegions = false; + eglConfig = chooseEglConfig(); + if (eglConfig == null) { + throw new RuntimeException("eglConfig not initialized"); + } + } else { + throw new RuntimeException("eglConfig not initialized"); + } + } + return eglConfig; + } + abstract ManagedEGLContext createManagedContext(EGLContext eglContext); private EGLConfig chooseEglConfig() { @@ -1104,6 +1289,11 @@ public abstract class HardwareRenderer { return mCanvas; } + @Override + void setName(String name) { + mName = name; + } + boolean canDraw() { return mGl != null && mCanvas != null; } @@ -1154,93 +1344,27 @@ public abstract class HardwareRenderer { mProfileLock.lock(); } - // We had to change the current surface and/or context, redraw everything - if (surfaceState == SURFACE_STATE_UPDATED) { - dirty = null; - beginFrame(null); - } else { - int[] size = mSurfaceSize; - beginFrame(size); - - if (size[1] != mHeight || size[0] != mWidth) { - mWidth = size[0]; - mHeight = size[1]; + dirty = beginFrame(canvas, dirty, surfaceState); - canvas.setViewport(mWidth, mHeight); - - dirty = null; - } - } + DisplayList displayList = buildDisplayList(view, canvas); int saveCount = 0; int status = DisplayList.STATUS_DONE; try { - view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) - == View.PFLAG_INVALIDATED; - view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; - - long getDisplayListStartTime = 0; - if (mProfileEnabled) { - mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; - if (mProfileCurrentFrame >= mProfileData.length) { - mProfileCurrentFrame = 0; - } - - getDisplayListStartTime = System.nanoTime(); - } - - canvas.clearLayerUpdates(); + status = prepareFrame(dirty); - DisplayList displayList; - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); - try { - displayList = view.getDisplayList(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame"); - try { - status = onPreDraw(dirty); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } saveCount = canvas.save(); callbacks.onHardwarePreDraw(canvas); - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - getDisplayListStartTime) * 0.000001f; - //noinspection PointlessArithmeticExpression - mProfileData[mProfileCurrentFrame] = total; - } - if (displayList != null) { - long drawDisplayListStartTime = 0; - if (mProfileEnabled) { - drawDisplayListStartTime = System.nanoTime(); - } - - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList"); - try { - status |= canvas.drawDisplayList(displayList, mRedrawClip, - DisplayList.FLAG_CLIP_CHILDREN); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - drawDisplayListStartTime) * 0.000001f; - mProfileData[mProfileCurrentFrame + 1] = total; - } - - handleFunctorStatus(attachInfo, status); + status = drawDisplayList(attachInfo, canvas, displayList, status); } else { // Shouldn't reach here view.draw(canvas); } + } catch (Exception e) { + Log.e(LOG_TAG, "An error has occurred while drawing:", e); } finally { callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); @@ -1248,43 +1372,19 @@ public abstract class HardwareRenderer { mFrameCount++; - if (mDebugDirtyRegions) { - if (mDebugPaint == null) { - mDebugPaint = new Paint(); - mDebugPaint.setColor(0x7fff0000); - } - - if (dirty != null && (mFrameCount & 1) == 0) { - canvas.drawRect(dirty, mDebugPaint); - } - } + debugDirtyRegions(dirty, canvas); + drawProfileData(attachInfo); } onPostDraw(); - attachInfo.mIgnoreDirtyState = false; - - if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) { - long eglSwapBuffersStartTime = 0; - if (mProfileEnabled) { - eglSwapBuffersStartTime = System.nanoTime(); - } - - sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); - - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - eglSwapBuffersStartTime) * 0.000001f; - mProfileData[mProfileCurrentFrame + 2] = total; - } - - checkEglErrors(); - } + swapBuffers(status); if (mProfileEnabled) { mProfileLock.unlock(); } + attachInfo.mIgnoreDirtyState = false; return dirty == null; } } @@ -1292,6 +1392,139 @@ public abstract class HardwareRenderer { return false; } + private DisplayList buildDisplayList(View view, HardwareCanvas canvas) { + view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) + == View.PFLAG_INVALIDATED; + view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; + + long buildDisplayListStartTime = startBuildDisplayListProfiling(); + canvas.clearLayerUpdates(); + + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); + DisplayList displayList = view.getDisplayList(); + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + + endBuildDisplayListProfiling(buildDisplayListStartTime); + + return displayList; + } + + abstract void drawProfileData(View.AttachInfo attachInfo); + + private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) { + // We had to change the current surface and/or context, redraw everything + if (surfaceState == SURFACE_STATE_UPDATED) { + dirty = null; + beginFrame(null); + } else { + int[] size = mSurfaceSize; + beginFrame(size); + + if (size[1] != mHeight || size[0] != mWidth) { + mWidth = size[0]; + mHeight = size[1]; + + canvas.setViewport(mWidth, mHeight); + + dirty = null; + } + } + + if (mDebugDataProvider != null) dirty = null; + + return dirty; + } + + private long startBuildDisplayListProfiling() { + if (mProfileEnabled) { + mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; + if (mProfileCurrentFrame >= mProfileData.length) { + mProfileCurrentFrame = 0; + } + + return System.nanoTime(); + } + return 0; + } + + private void endBuildDisplayListProfiling(long getDisplayListStartTime) { + if (mProfileEnabled) { + long now = System.nanoTime(); + float total = (now - getDisplayListStartTime) * 0.000001f; + //noinspection PointlessArithmeticExpression + mProfileData[mProfileCurrentFrame] = total; + } + } + + private int prepareFrame(Rect dirty) { + int status; + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame"); + try { + status = onPreDraw(dirty); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + return status; + } + + private int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas, + DisplayList displayList, int status) { + + long drawDisplayListStartTime = 0; + if (mProfileEnabled) { + drawDisplayListStartTime = System.nanoTime(); + } + + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList"); + try { + status |= canvas.drawDisplayList(displayList, mRedrawClip, + DisplayList.FLAG_CLIP_CHILDREN); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + + if (mProfileEnabled) { + long now = System.nanoTime(); + float total = (now - drawDisplayListStartTime) * 0.000001f; + mProfileData[mProfileCurrentFrame + 1] = total; + } + + handleFunctorStatus(attachInfo, status); + return status; + } + + private void swapBuffers(int status) { + if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) { + long eglSwapBuffersStartTime = 0; + if (mProfileEnabled) { + eglSwapBuffersStartTime = System.nanoTime(); + } + + sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); + + if (mProfileEnabled) { + long now = System.nanoTime(); + float total = (now - eglSwapBuffersStartTime) * 0.000001f; + mProfileData[mProfileCurrentFrame + 2] = total; + } + + checkEglErrors(); + } + } + + private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) { + if (mDebugDirtyRegions) { + if (mDebugPaint == null) { + mDebugPaint = new Paint(); + mDebugPaint.setColor(0x7fff0000); + } + + if (dirty != null && (mFrameCount & 1) == 0) { + canvas.drawRect(dirty, mDebugPaint); + } + } + } + private void handleFunctorStatus(View.AttachInfo attachInfo, int status) { // If the draw flag is set, functors will be invoked while executing // the tree of display lists @@ -1362,6 +1595,96 @@ public abstract class HardwareRenderer { } return SURFACE_STATE_SUCCESS; } + + private static int dpToPx(int dp, float density) { + return (int) (dp * density + 0.5f); + } + + class DrawPerformanceDataProvider extends GraphDataProvider { + private final int mGraphType; + + private int mVerticalUnit; + private int mHorizontalUnit; + private int mHorizontalMargin; + private int mThresholdStroke; + + DrawPerformanceDataProvider(int graphType) { + mGraphType = graphType; + } + + @Override + void prepare(DisplayMetrics metrics) { + final float density = metrics.density; + + mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); + mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density); + mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density); + mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); + } + + @Override + int getGraphType() { + return mGraphType; + } + + @Override + int getVerticalUnitSize() { + return mVerticalUnit; + } + + @Override + int getHorizontalUnitSize() { + return mHorizontalUnit; + } + + @Override + int getHorizontaUnitMargin() { + return mHorizontalMargin; + } + + @Override + float[] getData() { + return mProfileData; + } + + @Override + float getThreshold() { + return 16; + } + + @Override + int getFrameCount() { + return mProfileData.length / PROFILE_FRAME_DATA_COUNT; + } + + @Override + int getElementCount() { + return PROFILE_FRAME_DATA_COUNT; + } + + @Override + int getCurrentFrame() { + return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT; + } + + @Override + void setupGraphPaint(Paint paint, int elementIndex) { + paint.setColor(PROFILE_DRAW_COLORS[elementIndex]); + if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); + } + + @Override + void setupThresholdPaint(Paint paint) { + paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR); + paint.setStrokeWidth(mThresholdStroke); + } + + @Override + void setupCurrentFramePaint(Paint paint) { + paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR); + if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); + } + } } /** @@ -1370,6 +1693,8 @@ public abstract class HardwareRenderer { static class Gl20Renderer extends GlRenderer { private GLES20Canvas mGlCanvas; + private DisplayMetrics mDisplayMetrics; + private static EGLSurface sPbuffer; private static final Object[] sPbufferLock = new Object[0]; @@ -1436,6 +1761,10 @@ public abstract class HardwareRenderer { @Override int[] getConfig(boolean dirtyRegions) { + //noinspection PointlessBooleanExpression,ConstantConditions + final int stencilSize = GLES20Canvas.getStencilSize(); + final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + return new int[] { EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, @@ -1444,14 +1773,12 @@ public abstract class HardwareRenderer { EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_CONFIG_CAVEAT, EGL_NONE, - // TODO: Find a better way to choose the stencil size - EGL_STENCIL_SIZE, mShowOverdraw ? GLES20Canvas.getStencilSize() : 0, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | - (dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), + EGL_STENCIL_SIZE, stencilSize, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, EGL_NONE }; } - + @Override void initCaches() { GLES20Canvas.initCaches(); @@ -1473,6 +1800,153 @@ public abstract class HardwareRenderer { } @Override + void drawProfileData(View.AttachInfo attachInfo) { + if (mDebugDataProvider != null) { + final GraphDataProvider provider = mDebugDataProvider; + initProfileDrawData(attachInfo, provider); + + final int height = provider.getVerticalUnitSize(); + final int margin = provider.getHorizontaUnitMargin(); + final int width = provider.getHorizontalUnitSize(); + + int x = 0; + int count = 0; + int current = 0; + + final float[] data = provider.getData(); + final int elementCount = provider.getElementCount(); + final int graphType = provider.getGraphType(); + + int totalCount = provider.getFrameCount() * elementCount; + if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) { + totalCount -= elementCount; + } + + for (int i = 0; i < totalCount; i += elementCount) { + if (data[i] < 0.0f) break; + + int index = count * 4; + if (i == provider.getCurrentFrame() * elementCount) current = index; + + x += margin; + int x2 = x + width; + + int y2 = mHeight; + int y1 = (int) (y2 - data[i] * height); + + switch (graphType) { + case GraphDataProvider.GRAPH_TYPE_BARS: { + for (int j = 0; j < elementCount; j++) { + //noinspection MismatchedReadAndWriteOfArray + final float[] r = mProfileShapes[j]; + r[index] = x; + r[index + 1] = y1; + r[index + 2] = x2; + r[index + 3] = y2; + + y2 = y1; + if (j < elementCount - 1) { + y1 = (int) (y2 - data[i + j + 1] * height); + } + } + } break; + case GraphDataProvider.GRAPH_TYPE_LINES: { + for (int j = 0; j < elementCount; j++) { + //noinspection MismatchedReadAndWriteOfArray + final float[] r = mProfileShapes[j]; + r[index] = (x + x2) * 0.5f; + r[index + 1] = index == 0 ? y1 : r[index - 1]; + r[index + 2] = r[index] + width; + r[index + 3] = y1; + + y2 = y1; + if (j < elementCount - 1) { + y1 = (int) (y2 - data[i + j + 1] * height); + } + } + } break; + } + + + x += width; + count++; + } + + x += margin; + + drawGraph(graphType, count); + drawCurrentFrame(graphType, current); + drawThreshold(x, height); + } + } + + private void drawGraph(int graphType, int count) { + for (int i = 0; i < mProfileShapes.length; i++) { + mDebugDataProvider.setupGraphPaint(mProfilePaint, i); + switch (graphType) { + case GraphDataProvider.GRAPH_TYPE_BARS: + mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint); + break; + case GraphDataProvider.GRAPH_TYPE_LINES: + mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint); + break; + } + } + } + + private void drawCurrentFrame(int graphType, int index) { + if (index >= 0) { + mDebugDataProvider.setupCurrentFramePaint(mProfilePaint); + switch (graphType) { + case GraphDataProvider.GRAPH_TYPE_BARS: + mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1], + mProfileShapes[2][index + 2], mProfileShapes[0][index + 3], + mProfilePaint); + break; + case GraphDataProvider.GRAPH_TYPE_LINES: + mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1], + mProfileShapes[2][index], mHeight, mProfilePaint); + break; + } + } + } + + private void drawThreshold(int x, int height) { + float threshold = mDebugDataProvider.getThreshold(); + if (threshold > 0.0f) { + mDebugDataProvider.setupThresholdPaint(mProfilePaint); + int y = (int) (mHeight - threshold * height); + mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint); + } + } + + private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) { + if (mProfileShapes == null) { + final int elementCount = provider.getElementCount(); + final int frameCount = provider.getFrameCount(); + + mProfileShapes = new float[elementCount][]; + for (int i = 0; i < elementCount; i++) { + mProfileShapes[i] = new float[frameCount * 4]; + } + + mProfilePaint = new Paint(); + } + + mProfilePaint.reset(); + if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) { + mProfilePaint.setAntiAlias(true); + } + + if (mDisplayMetrics == null) { + mDisplayMetrics = new DisplayMetrics(); + } + + attachInfo.mDisplay.getMetrics(mDisplayMetrics); + provider.prepare(mDisplayMetrics); + } + + @Override void destroy(boolean full) { try { super.destroy(full); @@ -1484,14 +1958,6 @@ public abstract class HardwareRenderer { } @Override - void setup(int width, int height) { - super.setup(width, height); - if (mVsyncDisabled) { - disableVsync(); - } - } - - @Override void pushLayerUpdate(HardwareLayer layer) { mGlCanvas.pushLayerUpdate(layer); } @@ -1507,12 +1973,12 @@ public abstract class HardwareRenderer { } @Override - HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { + public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { return new GLES20RenderLayer(width, height, isOpaque); } @Override - SurfaceTexture createSurfaceTexture(HardwareLayer layer) { + public SurfaceTexture createSurfaceTexture(HardwareLayer layer) { return ((GLES20TextureLayer) layer).getSurfaceTexture(); } diff --git a/core/java/android/view/IDisplayContentChangeListener.aidl b/core/java/android/view/IMagnificationCallbacks.aidl index ef7edea..032d073 100644 --- a/core/java/android/view/IDisplayContentChangeListener.aidl +++ b/core/java/android/view/IMagnificationCallbacks.aidl @@ -1,7 +1,7 @@ /* ** Copyright 2012, The Android Open Source Project ** -** Licensed under the Apache License, Version 2.0 (the "License") +** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** @@ -16,18 +16,14 @@ package android.view; -import android.os.IBinder; -import android.view.WindowInfo; -import android.graphics.Rect; +import android.graphics.Region; /** - * Interface for observing content changes on a display. - * * {@hide} */ -oneway interface IDisplayContentChangeListener { - void onWindowTransition(int displayId, int transition, in WindowInfo info); - void onRectangleOnScreenRequested(int displayId, in Rect rectangle, boolean immediate); - void onWindowLayersChanged(int displayId); +oneway interface IMagnificationCallbacks { + void onMagnifedBoundsChanged(in Region bounds); + void onRectangleOnScreenRequested(int left, int top, int right, int bottom); void onRotationChanged(int rotation); + void onUserContextChanged(); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 2b6cbcf..e4ecb5c 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -27,17 +27,17 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IRemoteCallback; import android.view.IApplicationToken; -import android.view.IDisplayContentChangeListener; +import android.view.IMagnificationCallbacks; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; import android.view.KeyEvent; import android.view.InputEvent; +import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.InputChannel; import android.view.InputDevice; import android.view.IInputFilter; -import android.view.WindowInfo; /** * System private interface to the window manager. @@ -65,6 +65,8 @@ interface IWindowManager void setForcedDisplayDensity(int displayId, int density); void clearForcedDisplayDensity(int displayId); + void setOverscan(int displayId, int left, int top, int right, int bottom); + // Is the device configured to have a full system bar for larger screens? boolean hasSystemNavBar(); @@ -74,7 +76,7 @@ interface IWindowManager void setEventDispatching(boolean enabled); void addWindowToken(IBinder token, int type); void removeWindowToken(IBinder token); - void addAppToken(int addPos, int userId, IApplicationToken token, + void addAppToken(int addPos, IApplicationToken token, int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked); void setAppGroupId(IBinder token, int groupId); void setAppOrientation(IApplicationToken token, int requestedOrientation); @@ -97,9 +99,6 @@ interface IWindowManager void startAppFreezingScreen(IBinder token, int configChanges); void stopAppFreezingScreen(IBinder token, boolean force); void removeAppToken(IBinder token); - void moveAppToken(int index, IBinder token); - void moveAppTokensToTop(in List<IBinder> tokens); - void moveAppTokensToBottom(in List<IBinder> tokens); // Re-evaluate the current orientation from the caller's state. // If there is a change, the new Configuration is returned and the @@ -190,6 +189,13 @@ interface IWindowManager void thawRotation(); /** + * Gets whether the rotation is frozen. + * + * @return Whether the rotation is frozen. + */ + boolean isRotationFrozen(); + + /** * Create a screenshot of the applications currently displayed. */ Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight); @@ -221,48 +227,49 @@ interface IWindowManager IBinder getFocusedWindowToken(); /** - * Gets the compatibility scale of e window given its token. - */ - float getWindowCompatibilityScale(IBinder windowToken); - - /** * Sets an input filter for manipulating the input event stream. */ void setInputFilter(in IInputFilter filter); /** - * Sets the scale and offset for implementing accessibility magnification. - */ - void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY); - - /** - * Adds a listener for display content changes. + * Gets the frame of a window given its token. */ - void addDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener); + void getWindowFrame(IBinder token, out Rect outFrame); /** - * Removes a listener for display content changes. + * Device is in safe mode. */ - void removeDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener); + boolean isSafeModeEnabled(); /** - * Gets the info for a window given its token. + * Tell keyguard to show the assistant (Intent.ACTION_ASSIST) after asking for the user's + * credentials. */ - WindowInfo getWindowInfo(IBinder token); + void showAssistant(); /** - * Gets the infos for all visible windows. + * Sets the display magnification callbacks. These callbacks notify + * the client for contextual changes related to display magnification. + * + * @param callbacks The magnification callbacks. */ - void getVisibleWindowsForDisplay(int displayId, out List<WindowInfo> outInfos); + void setMagnificationCallbacks(IMagnificationCallbacks callbacks); /** - * Device is in safe mode. + * Sets the magnification spec to be applied to all windows that can be + * magnified. + * + * @param spec The current magnification spec. */ - boolean isSafeModeEnabled(); + void setMagnificationSpec(in MagnificationSpec spec); /** - * Tell keyguard to show the assistant (Intent.ACTION_ASSIST) after asking for the user's - * credentials. + * Gets the magnification spec for a window given its token. If the + * window has a compatibility scale it is also folded in the returned + * magnification spec. + * + * @param windowToken The unique window token. + * @return The magnification spec if such or null. */ - void showAssistant(); + MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken); } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index c2a3e58..bb533bf 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -302,27 +302,27 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana) /** Key code constant: A Button key. * On a game controller, the A button should be either the button labeled A - * or the first button on the upper row of controller buttons. */ + * or the first button on the bottom row of controller buttons. */ public static final int KEYCODE_BUTTON_A = 96; /** Key code constant: B Button key. * On a game controller, the B button should be either the button labeled B - * or the second button on the upper row of controller buttons. */ + * or the second button on the bottom row of controller buttons. */ public static final int KEYCODE_BUTTON_B = 97; /** Key code constant: C Button key. * On a game controller, the C button should be either the button labeled C - * or the third button on the upper row of controller buttons. */ + * or the third button on the bottom row of controller buttons. */ public static final int KEYCODE_BUTTON_C = 98; /** Key code constant: X Button key. * On a game controller, the X button should be either the button labeled X - * or the first button on the lower row of controller buttons. */ + * or the first button on the upper row of controller buttons. */ public static final int KEYCODE_BUTTON_X = 99; /** Key code constant: Y Button key. * On a game controller, the Y button should be either the button labeled Y - * or the second button on the lower row of controller buttons. */ + * or the second button on the upper row of controller buttons. */ public static final int KEYCODE_BUTTON_Y = 100; /** Key code constant: Z Button key. * On a game controller, the Z button should be either the button labeled Z - * or the third button on the lower row of controller buttons. */ + * or the third button on the upper row of controller buttons. */ public static final int KEYCODE_BUTTON_Z = 101; /** Key code constant: L1 Button key. * On a game controller, the L1 button should be either the button labeled L1 (or L) @@ -623,8 +623,14 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: Assist key. * Launches the global assist activity. Not delivered to applications. */ public static final int KEYCODE_ASSIST = 219; + /** Key code constant: Brightness Down key. + * Adjusts the screen brightness down. */ + public static final int KEYCODE_BRIGHTNESS_DOWN = 220; + /** Key code constant: Brightness Up key. + * Adjusts the screen brightness up. */ + public static final int KEYCODE_BRIGHTNESS_UP = 221; - private static final int LAST_KEYCODE = KEYCODE_ASSIST; + private static final int LAST_KEYCODE = KEYCODE_BRIGHTNESS_UP; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -866,6 +872,8 @@ public class KeyEvent extends InputEvent implements Parcelable { names.append(KEYCODE_RO, "KEYCODE_RO"); names.append(KEYCODE_KANA, "KEYCODE_KANA"); names.append(KEYCODE_ASSIST, "KEYCODE_ASSIST"); + names.append(KEYCODE_BRIGHTNESS_DOWN, "KEYCODE_BRIGHTNESS_DOWN"); + names.append(KEYCODE_BRIGHTNESS_UP, "KEYCODE_BRIGHTNESS_UP"); }; // Symbolic names of all metakeys in bit order from least significant to most significant. diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/MagnificationSpec.aidl index 23e927a..d5fbdef 100644 --- a/core/java/android/view/WindowInfo.aidl +++ b/core/java/android/view/MagnificationSpec.aidl @@ -17,4 +17,4 @@ package android.view; -parcelable WindowInfo; +parcelable MagnificationSpec; diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java new file mode 100644 index 0000000..0ee6714 --- /dev/null +++ b/core/java/android/view/MagnificationSpec.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Pools.SynchronizedPool; + +/** + * This class represents spec for performing screen magnification. + * + * @hide + */ +public class MagnificationSpec implements Parcelable { + private static final int MAX_POOL_SIZE = 20; + private static final SynchronizedPool<MagnificationSpec> sPool = + new SynchronizedPool<MagnificationSpec>(MAX_POOL_SIZE); + + public float scale = 1.0f; + public float offsetX; + public float offsetY; + + private MagnificationSpec() { + /* do nothing - reducing visibility */ + } + + public void initialize(float scale, float offsetX, float offsetY) { + if (scale < 1) { + throw new IllegalArgumentException("Scale must be greater than or equal to one!"); + } + this.scale = scale; + this.offsetX = offsetX; + this.offsetY = offsetY; + } + + public boolean isNop() { + return scale == 1.0f && offsetX == 0 && offsetY == 0; + } + + public static MagnificationSpec obtain(MagnificationSpec other) { + MagnificationSpec info = obtain(); + info.scale = other.scale; + info.offsetX = other.offsetX; + info.offsetY = other.offsetY; + return info; + } + + public static MagnificationSpec obtain() { + MagnificationSpec spec = sPool.acquire(); + return (spec != null) ? spec : new MagnificationSpec(); + } + + public void recycle() { + clear(); + sPool.release(this); + } + + public void clear() { + scale = 1.0f; + offsetX = 0.0f; + offsetY = 0.0f; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeFloat(scale); + parcel.writeFloat(offsetX); + parcel.writeFloat(offsetY); + recycle(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("<scale:"); + builder.append(scale); + builder.append(",offsetX:"); + builder.append(offsetX); + builder.append(",offsetY:"); + builder.append(offsetY); + builder.append(">"); + return builder.toString(); + } + + private void initFromParcel(Parcel parcel) { + scale = parcel.readFloat(); + offsetX = parcel.readFloat(); + offsetY = parcel.readFloat(); + } + + public static final Creator<MagnificationSpec> CREATOR = new Creator<MagnificationSpec>() { + @Override + public MagnificationSpec[] newArray(int size) { + return new MagnificationSpec[size]; + } + + @Override + public MagnificationSpec createFromParcel(Parcel parcel) { + MagnificationSpec spec = MagnificationSpec.obtain(); + spec.initFromParcel(parcel); + return spec; + } + }; +} diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java index b03e4c7..883fd49 100644 --- a/core/java/android/view/SimulatedDpad.java +++ b/core/java/android/view/SimulatedDpad.java @@ -186,7 +186,7 @@ class SimulatedDpad { Intent intent = ((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE)) - .getAssistIntent(mContext, UserHandle.USER_CURRENT_OR_SELF); + .getAssistIntent(mContext, false, UserHandle.USER_CURRENT_OR_SELF); if (intent != null) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 0a81a71..de64e14 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -16,20 +16,15 @@ package android.view; -import dalvik.system.CloseGuard; - import android.content.res.CompatibilityInfo.Translator; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; -import android.graphics.Region; import android.graphics.SurfaceTexture; -import android.os.IBinder; -import android.os.Parcelable; import android.os.Parcel; -import android.os.SystemProperties; +import android.os.Parcelable; import android.util.Log; +import dalvik.system.CloseGuard; /** * Handle onto a raw buffer that is being managed by the screen compositor. @@ -37,8 +32,19 @@ import android.util.Log; public class Surface implements Parcelable { private static final String TAG = "Surface"; - private static final boolean HEADLESS = "1".equals( - SystemProperties.get("ro.config.headless", "0")); + private static native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture) + throws OutOfResourcesException; + + private native Canvas nativeLockCanvas(int nativeObject, Rect dirty); + private native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas); + + private static native void nativeRelease(int nativeObject); + private static native void nativeDestroy(int nativeObject); + private static native boolean nativeIsValid(int nativeObject); + private static native boolean nativeIsConsumerRunningBehind(int nativeObject); + private static native int nativeCopyFrom(int nativeObject, int surfaceControlNativeObject); + private static native int nativeReadFromParcel(int nativeObject, Parcel source); + private static native void nativeWriteToParcel(int nativeObject, Parcel dest); public static final Parcelable.Creator<Surface> CREATOR = new Parcelable.Creator<Surface>() { @@ -52,157 +58,11 @@ public class Surface implements Parcelable { return null; } } - public Surface[] newArray(int size) { return new Surface[size]; } }; - /** - * Rotation constant: 0 degree rotation (natural orientation) - */ - public static final int ROTATION_0 = 0; - - /** - * Rotation constant: 90 degree rotation. - */ - public static final int ROTATION_90 = 1; - - /** - * Rotation constant: 180 degree rotation. - */ - public static final int ROTATION_180 = 2; - - /** - * Rotation constant: 270 degree rotation. - */ - public static final int ROTATION_270 = 3; - - /* built-in physical display ids (keep in sync with ISurfaceComposer.h) - * these are different from the logical display ids used elsewhere in the framework */ - - /** - * Built-in physical display id: Main display. - * Use only with {@link #getBuiltInDisplay()}. - * @hide - */ - public static final int BUILT_IN_DISPLAY_ID_MAIN = 0; - - /** - * Built-in physical display id: Attached HDMI display. - * Use only with {@link #getBuiltInDisplay()}. - * @hide - */ - public static final int BUILT_IN_DISPLAY_ID_HDMI = 1; - - /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */ - - /** - * Surface creation flag: Surface is created hidden - * @hide */ - public static final int HIDDEN = 0x00000004; - - /** - * Surface creation flag: The surface contains secure content, special - * measures will be taken to disallow the surface's content to be copied - * from another process. In particular, screenshots and VNC servers will - * be disabled, but other measures can take place, for instance the - * surface might not be hardware accelerated. - * @hide - */ - public static final int SECURE = 0x00000080; - - /** - * Surface creation flag: Creates a surface where color components are interpreted - * as "non pre-multiplied" by their alpha channel. Of course this flag is - * meaningless for surfaces without an alpha channel. By default - * surfaces are pre-multiplied, which means that each color component is - * already multiplied by its alpha value. In this case the blending - * equation used is: - * - * DEST = SRC + DEST * (1-SRC_ALPHA) - * - * By contrast, non pre-multiplied surfaces use the following equation: - * - * DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA) - * - * pre-multiplied surfaces must always be used if transparent pixels are - * composited on top of each-other into the surface. A pre-multiplied - * surface can never lower the value of the alpha component of a given - * pixel. - * - * In some rare situations, a non pre-multiplied surface is preferable. - * @hide - */ - public static final int NON_PREMULTIPLIED = 0x00000100; - - /** - * Surface creation flag: Indicates that the surface must be considered opaque, - * even if its pixel format is set to translucent. This can be useful if an - * application needs full RGBA 8888 support for instance but will - * still draw every pixel opaque. - * @hide - */ - public static final int OPAQUE = 0x00000400; - - /** - * Surface creation flag: Application requires a hardware-protected path to an - * external display sink. If a hardware-protected path is not available, - * then this surface will not be displayed on the external sink. - * @hide - */ - public static final int PROTECTED_APP = 0x00000800; - - // 0x1000 is reserved for an independent DRM protected flag in framework - - /** - * Surface creation flag: Creates a normal surface. - * This is the default. - * @hide - */ - public static final int FX_SURFACE_NORMAL = 0x00000000; - - /** - * Surface creation flag: Creates a Blur surface. - * Everything behind this surface is blurred by some amount. - * The quality and refresh speed of the blur effect is not settable or guaranteed. - * It is an error to lock a Blur surface, since it doesn't have a backing store. - * @hide - * @deprecated - */ - @Deprecated - public static final int FX_SURFACE_BLUR = 0x00010000; - - /** - * Surface creation flag: Creates a Dim surface. - * Everything behind this surface is dimmed by the amount specified - * in {@link #setAlpha}. It is an error to lock a Dim surface, since it - * doesn't have a backing store. - * @hide - */ - public static final int FX_SURFACE_DIM = 0x00020000; - - /** - * @hide - */ - public static final int FX_SURFACE_SCREENSHOT = 0x00030000; - - /** - * Mask used for FX values above. - * @hide - */ - public static final int FX_SURFACE_MASK = 0x000F0000; - - /* flags used with setFlags() (keep in sync with ISurfaceComposer.h) */ - - /** - * Surface flag: Hide the surface. - * Equivalent to calling hide(). - * @hide - */ - public static final int SURFACE_HIDDEN = 0x01; - - private final CloseGuard mCloseGuard = CloseGuard.get(); private String mName; @@ -211,8 +71,8 @@ public class Surface implements Parcelable { // server or system processes. When this class is parceled we defer to the // mSurfaceControl to do the parceling. Otherwise we parcel the // mNativeSurface. - private int mNativeSurface; // Surface* - private int mNativeSurfaceControl; // SurfaceControl* + int mNativeObject; // package scope only for SurfaceControl access + private int mGenerationId; // incremented each time mNativeSurface changes private final Canvas mCanvas = new CompatibleCanvas(); private int mCanvasSaveCount; // Canvas save count at time of lockCanvas() @@ -225,118 +85,34 @@ public class Surface implements Parcelable { // non compatibility mode. private Matrix mCompatibleMatrix; - private int mWidth; - private int mHeight; - - private native void nativeCreate(SurfaceSession session, String name, - int w, int h, int format, int flags) - throws OutOfResourcesException; - private native void nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture) - throws OutOfResourcesException; - private native void nativeRelease(); - private native void nativeDestroy(); - - private native boolean nativeIsValid(); - private native int nativeGetIdentity(); - private native boolean nativeIsConsumerRunningBehind(); - - private native Canvas nativeLockCanvas(Rect dirty); - private native void nativeUnlockCanvasAndPost(Canvas canvas); - - private static native Bitmap nativeScreenshot(IBinder displayToken, - int width, int height, int minLayer, int maxLayer, boolean allLayers); - - private static native void nativeOpenTransaction(); - private static native void nativeCloseTransaction(); - private static native void nativeSetAnimationTransaction(); - - private native void nativeSetLayer(int zorder); - private native void nativeSetPosition(float x, float y); - private native void nativeSetSize(int w, int h); - private native void nativeSetTransparentRegionHint(Region region); - private native void nativeSetAlpha(float alpha); - private native void nativeSetMatrix(float dsdx, float dtdx, float dsdy, float dtdy); - private native void nativeSetFlags(int flags, int mask); - private native void nativeSetWindowCrop(Rect crop); - private native void nativeSetLayerStack(int layerStack); - - private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId); - private static native IBinder nativeCreateDisplay(String name, boolean secure); - private static native void nativeSetDisplaySurface( - IBinder displayToken, Surface surface); - private static native void nativeSetDisplayLayerStack( - IBinder displayToken, int layerStack); - private static native void nativeSetDisplayProjection( - IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect); - private static native boolean nativeGetDisplayInfo( - IBinder displayToken, PhysicalDisplayInfo outInfo); - private static native void nativeBlankDisplay(IBinder displayToken); - private static native void nativeUnblankDisplay(IBinder displayToken); - - private native void nativeCopyFrom(Surface other); - private native void nativeTransferFrom(Surface other); - private native void nativeReadFromParcel(Parcel source); - private native void nativeWriteToParcel(Parcel dest); - /** - * Create an empty surface, which will later be filled in by readFromParcel(). - * @hide + * Rotation constant: 0 degree rotation (natural orientation) */ - public Surface() { - checkHeadless(); + public static final int ROTATION_0 = 0; - mCloseGuard.open("release"); - } + /** + * Rotation constant: 90 degree rotation. + */ + public static final int ROTATION_90 = 1; /** - * Create a surface with a name. - * - * The surface creation flags specify what kind of surface to create and - * certain options such as whether the surface can be assumed to be opaque - * and whether it should be initially hidden. Surfaces should always be - * created with the {@link #HIDDEN} flag set to ensure that they are not - * made visible prematurely before all of the surface's properties have been - * configured. - * - * Good practice is to first create the surface with the {@link #HIDDEN} flag - * specified, open a transaction, set the surface layer, layer stack, alpha, - * and position, call {@link #show} if appropriate, and close the transaction. - * - * @param session The surface session, must not be null. - * @param name The surface name, must not be null. - * @param w The surface initial width. - * @param h The surface initial height. - * @param flags The surface creation flags. Should always include {@link #HIDDEN} - * in the creation flags. - * @hide + * Rotation constant: 180 degree rotation. */ - public Surface(SurfaceSession session, - String name, int w, int h, int format, int flags) - throws OutOfResourcesException { - if (session == null) { - throw new IllegalArgumentException("session must not be null"); - } - if (name == null) { - throw new IllegalArgumentException("name must not be null"); - } + public static final int ROTATION_180 = 2; - if ((flags & HIDDEN) == 0) { - Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set " - + "to ensure that they are not made visible prematurely before " - + "all of the surface's properties have been configured. " - + "Set the other properties and make the surface visible within " - + "a transaction. New surface name: " + name, - new Throwable()); - } + /** + * Rotation constant: 270 degree rotation. + */ + public static final int ROTATION_270 = 3; - checkHeadless(); - mName = name; - mWidth = w; - mHeight = h; - nativeCreate(session, name, w, h, format, flags); + /** + * Create an empty surface, which will later be filled in by readFromParcel(). + * @hide + */ + public Surface() { mCloseGuard.open("release"); } @@ -355,11 +131,9 @@ public class Surface implements Parcelable { throw new IllegalArgumentException("surfaceTexture must not be null"); } - checkHeadless(); - mName = surfaceTexture.toString(); try { - nativeCreateFromSurfaceTexture(surfaceTexture); + mNativeObject = nativeCreateFromSurfaceTexture(surfaceTexture); } catch (OutOfResourcesException ex) { // We can't throw OutOfResourcesException because it would be an API change. throw new RuntimeException(ex); @@ -368,13 +142,20 @@ public class Surface implements Parcelable { mCloseGuard.open("release"); } + private Surface(int nativeObject) { + mNativeObject = nativeObject; + mCloseGuard.open("release"); + } + @Override protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } - nativeRelease(); + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + } } finally { super.finalize(); } @@ -386,7 +167,10 @@ public class Surface implements Parcelable { * This will make the surface invalid. */ public void release() { - nativeRelease(); + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + mNativeObject = 0; + } mCloseGuard.close(); } @@ -397,7 +181,10 @@ public class Surface implements Parcelable { * @hide */ public void destroy() { - nativeDestroy(); + if (mNativeObject != 0) { + nativeDestroy(mNativeObject); + mNativeObject = 0; + } mCloseGuard.close(); } @@ -408,7 +195,8 @@ public class Surface implements Parcelable { * Otherwise returns false. */ public boolean isValid() { - return nativeIsValid(); + if (mNativeObject == 0) return false; + return nativeIsValid(mNativeObject); } /** @@ -429,7 +217,8 @@ public class Surface implements Parcelable { * @hide */ public boolean isConsumerRunningBehind() { - return nativeIsConsumerRunningBehind(); + checkNotReleased(); + return nativeIsConsumerRunningBehind(mNativeObject); } /** @@ -438,7 +227,7 @@ public class Surface implements Parcelable { * After drawing into the provided {@link Canvas}, the caller should * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. * - * @param dirty A rectangle that represents the dirty region that the caller wants + * @param inOutDirty A rectangle that represents the dirty region that the caller wants * to redraw. This function may choose to expand the dirty rectangle if for example * the surface has been resized or if the previous contents of the surface were * not available. The caller should redraw the entire dirty region as represented @@ -447,9 +236,10 @@ public class Surface implements Parcelable { * entire surface should be redrawn. * @return A canvas for drawing into the surface. */ - public Canvas lockCanvas(Rect dirty) + public Canvas lockCanvas(Rect inOutDirty) throws OutOfResourcesException, IllegalArgumentException { - return nativeLockCanvas(dirty); + checkNotReleased(); + return nativeLockCanvas(mNativeObject, inOutDirty); } /** @@ -459,7 +249,8 @@ public class Surface implements Parcelable { * @param canvas The canvas previously obtained from {@link #lockCanvas}. */ public void unlockCanvasAndPost(Canvas canvas) { - nativeUnlockCanvasAndPost(canvas); + checkNotReleased(); + nativeUnlockCanvasAndPost(mNativeObject, canvas); } /** @@ -482,202 +273,6 @@ public class Surface implements Parcelable { } } - /** - * Like {@link #screenshot(int, int, int, int)} but includes all - * Surfaces in the screenshot. - * - * @hide - */ - public static Bitmap screenshot(int width, int height) { - // TODO: should take the display as a parameter - IBinder displayToken = getBuiltInDisplay(BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, width, height, 0, 0, true); - } - - /** - * Copy the current screen contents into a bitmap and return it. - * - * @param width The desired width of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param height The desired height of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param minLayer The lowest (bottom-most Z order) surface layer to - * include in the screenshot. - * @param maxLayer The highest (top-most Z order) surface layer to - * include in the screenshot. - * @return Returns a Bitmap containing the screen contents, or null - * if an error occurs. - * - * @hide - */ - public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) { - // TODO: should take the display as a parameter - IBinder displayToken = getBuiltInDisplay(BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false); - } - - /* - * set surface parameters. - * needs to be inside open/closeTransaction block - */ - - /** start a transaction @hide */ - public static void openTransaction() { - nativeOpenTransaction(); - } - - /** end a transaction @hide */ - public static void closeTransaction() { - nativeCloseTransaction(); - } - - /** flag the transaction as an animation @hide */ - public static void setAnimationTransaction() { - nativeSetAnimationTransaction(); - } - - /** @hide */ - public void setLayer(int zorder) { - nativeSetLayer(zorder); - } - - /** @hide */ - public void setPosition(int x, int y) { - nativeSetPosition(x, y); - } - - /** @hide */ - public void setPosition(float x, float y) { - nativeSetPosition(x, y); - } - - /** @hide */ - public void setSize(int w, int h) { - mWidth = w; - mHeight = h; - nativeSetSize(w, h); - } - - /** @hide */ - public int getWidth() { - return mWidth; - } - - /** @hide */ - public int getHeight() { - return mHeight; - } - - /** @hide */ - public void hide() { - nativeSetFlags(SURFACE_HIDDEN, SURFACE_HIDDEN); - } - - /** @hide */ - public void show() { - nativeSetFlags(0, SURFACE_HIDDEN); - } - - /** @hide */ - public void setTransparentRegionHint(Region region) { - nativeSetTransparentRegionHint(region); - } - - /** @hide */ - public void setAlpha(float alpha) { - nativeSetAlpha(alpha); - } - - /** @hide */ - public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - nativeSetMatrix(dsdx, dtdx, dsdy, dtdy); - } - - /** @hide */ - public void setFlags(int flags, int mask) { - nativeSetFlags(flags, mask); - } - - /** @hide */ - public void setWindowCrop(Rect crop) { - nativeSetWindowCrop(crop); - } - - /** @hide */ - public void setLayerStack(int layerStack) { - nativeSetLayerStack(layerStack); - } - - /** @hide */ - public static IBinder getBuiltInDisplay(int builtInDisplayId) { - return nativeGetBuiltInDisplay(builtInDisplayId); - } - - /** @hide */ - public static IBinder createDisplay(String name, boolean secure) { - if (name == null) { - throw new IllegalArgumentException("name must not be null"); - } - return nativeCreateDisplay(name, secure); - } - - /** @hide */ - public static void setDisplaySurface(IBinder displayToken, Surface surface) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - nativeSetDisplaySurface(displayToken, surface); - } - - /** @hide */ - public static void setDisplayLayerStack(IBinder displayToken, int layerStack) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - nativeSetDisplayLayerStack(displayToken, layerStack); - } - - /** @hide */ - public static void setDisplayProjection(IBinder displayToken, - int orientation, Rect layerStackRect, Rect displayRect) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - if (layerStackRect == null) { - throw new IllegalArgumentException("layerStackRect must not be null"); - } - if (displayRect == null) { - throw new IllegalArgumentException("displayRect must not be null"); - } - nativeSetDisplayProjection(displayToken, orientation, layerStackRect, displayRect); - } - - /** @hide */ - public static boolean getDisplayInfo(IBinder displayToken, PhysicalDisplayInfo outInfo) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - if (outInfo == null) { - throw new IllegalArgumentException("outInfo must not be null"); - } - return nativeGetDisplayInfo(displayToken, outInfo); - } - - /** @hide */ - public static void blankDisplay(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - nativeBlankDisplay(displayToken); - } - - /** @hide */ - public static void unblankDisplay(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - nativeUnblankDisplay(displayToken); - } /** * Copy another surface to this one. This surface now holds a reference @@ -688,13 +283,15 @@ public class Surface implements Parcelable { * in to it. * @hide */ - public void copyFrom(Surface other) { + public void copyFrom(SurfaceControl other) { if (other == null) { throw new IllegalArgumentException("other must not be null"); } - if (other != this) { - nativeCopyFrom(other); + if (other.mNativeObject == 0) { + throw new NullPointerException( + "SurfaceControl native object is null. Are you using a released SurfaceControl?"); } + mNativeObject = nativeCopyFrom(mNativeObject, other.mNativeObject); } /** @@ -710,7 +307,13 @@ public class Surface implements Parcelable { throw new IllegalArgumentException("other must not be null"); } if (other != this) { - nativeTransferFrom(other); + if (mNativeObject != 0) { + // release our reference to our native object + nativeRelease(mNativeObject); + } + // transfer the reference from other to us + mNativeObject = other.mNativeObject; + other.mNativeObject = 0; } } @@ -723,9 +326,8 @@ public class Surface implements Parcelable { if (source == null) { throw new IllegalArgumentException("source must not be null"); } - mName = source.readString(); - nativeReadFromParcel(source); + mNativeObject = nativeReadFromParcel(mNativeObject, source); } @Override @@ -733,9 +335,8 @@ public class Surface implements Parcelable { if (dest == null) { throw new IllegalArgumentException("dest must not be null"); } - dest.writeString(mName); - nativeWriteToParcel(dest); + nativeWriteToParcel(mNativeObject, dest); if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { release(); } @@ -743,13 +344,7 @@ public class Surface implements Parcelable { @Override public String toString() { - return "Surface(name=" + mName + ", identity=" + nativeGetIdentity() + ")"; - } - - private static void checkHeadless() { - if (HEADLESS) { - throw new UnsupportedOperationException("Device is headless"); - } + return "Surface(name=" + mName + ")"; } /** @@ -758,69 +353,36 @@ public class Surface implements Parcelable { public static class OutOfResourcesException extends Exception { public OutOfResourcesException() { } - public OutOfResourcesException(String name) { super(name); } } /** - * Describes the properties of a physical display known to surface flinger. + * Returns a human readable representation of a rotation. + * + * @param rotation The rotation. + * @return The rotation symbolic name. + * * @hide */ - public static final class PhysicalDisplayInfo { - public int width; - public int height; - public float refreshRate; - public float density; - public float xDpi; - public float yDpi; - public boolean secure; - - public PhysicalDisplayInfo() { - } - - public PhysicalDisplayInfo(PhysicalDisplayInfo other) { - copyFrom(other); - } - - @Override - public boolean equals(Object o) { - return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o); - } - - public boolean equals(PhysicalDisplayInfo other) { - return other != null - && width == other.width - && height == other.height - && refreshRate == other.refreshRate - && density == other.density - && xDpi == other.xDpi - && yDpi == other.yDpi - && secure == other.secure; - } - - @Override - public int hashCode() { - return 0; // don't care - } - - public void copyFrom(PhysicalDisplayInfo other) { - width = other.width; - height = other.height; - refreshRate = other.refreshRate; - density = other.density; - xDpi = other.xDpi; - yDpi = other.yDpi; - secure = other.secure; - } - - // For debugging purposes - @Override - public String toString() { - return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, " - + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure - + "}"; + public static String rotationToString(int rotation) { + switch (rotation) { + case Surface.ROTATION_0: { + return "ROTATION_0"; + } + case Surface.ROTATION_90: { + return "ROATATION_90"; + } + case Surface.ROTATION_180: { + return "ROATATION_180"; + } + case Surface.ROTATION_270: { + return "ROATATION_270"; + } + default: { + throw new IllegalArgumentException("Invalid rotation: " + rotation); + } } } @@ -883,4 +445,9 @@ public class Surface implements Parcelable { mOrigMatrix.set(m); } } + + private void checkNotReleased() { + if (mNativeObject == 0) throw new NullPointerException( + "mNativeObject is null. Have you called release() already?"); + } } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java new file mode 100644 index 0000000..9f50065 --- /dev/null +++ b/core/java/android/view/SurfaceControl.java @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import dalvik.system.CloseGuard; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.IBinder; +import android.os.SystemProperties; +import android.util.Log; + +/** + * SurfaceControl + * @hide + */ +public class SurfaceControl { + private static final String TAG = "SurfaceControl"; + + private static native int nativeCreate(SurfaceSession session, String name, + int w, int h, int format, int flags) + throws OutOfResourcesException; + private static native void nativeRelease(int nativeObject); + private static native void nativeDestroy(int nativeObject); + + private static native Bitmap nativeScreenshot(IBinder displayToken, + int width, int height, int minLayer, int maxLayer, boolean allLayers); + + private static native void nativeOpenTransaction(); + private static native void nativeCloseTransaction(); + private static native void nativeSetAnimationTransaction(); + + private static native void nativeSetLayer(int nativeObject, int zorder); + private static native void nativeSetPosition(int nativeObject, float x, float y); + private static native void nativeSetSize(int nativeObject, int w, int h); + private static native void nativeSetTransparentRegionHint(int nativeObject, Region region); + private static native void nativeSetAlpha(int nativeObject, float alpha); + private static native void nativeSetMatrix(int nativeObject, float dsdx, float dtdx, float dsdy, float dtdy); + private static native void nativeSetFlags(int nativeObject, int flags, int mask); + private static native void nativeSetWindowCrop(int nativeObject, int l, int t, int r, int b); + private static native void nativeSetLayerStack(int nativeObject, int layerStack); + + private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId); + private static native IBinder nativeCreateDisplay(String name, boolean secure); + private static native void nativeSetDisplaySurface( + IBinder displayToken, int nativeSurfaceObject); + private static native void nativeSetDisplayLayerStack( + IBinder displayToken, int layerStack); + private static native void nativeSetDisplayProjection( + IBinder displayToken, int orientation, + int l, int t, int r, int b, + int L, int T, int R, int B); + private static native boolean nativeGetDisplayInfo( + IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo); + private static native void nativeBlankDisplay(IBinder displayToken); + private static native void nativeUnblankDisplay(IBinder displayToken); + + + private final CloseGuard mCloseGuard = CloseGuard.get(); + private String mName; + int mNativeObject; // package visibility only for Surface.java access + + private static final boolean HEADLESS = "1".equals( + SystemProperties.get("ro.config.headless", "0")); + + /** + * Exception thrown when a surface couldn't be created or resized. + */ + public static class OutOfResourcesException extends Exception { + public OutOfResourcesException() { + } + public OutOfResourcesException(String name) { + super(name); + } + } + + /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */ + + /** + * Surface creation flag: Surface is created hidden + */ + public static final int HIDDEN = 0x00000004; + + /** + * Surface creation flag: The surface contains secure content, special + * measures will be taken to disallow the surface's content to be copied + * from another process. In particular, screenshots and VNC servers will + * be disabled, but other measures can take place, for instance the + * surface might not be hardware accelerated. + * + */ + public static final int SECURE = 0x00000080; + + /** + * Surface creation flag: Creates a surface where color components are interpreted + * as "non pre-multiplied" by their alpha channel. Of course this flag is + * meaningless for surfaces without an alpha channel. By default + * surfaces are pre-multiplied, which means that each color component is + * already multiplied by its alpha value. In this case the blending + * equation used is: + * + * DEST = SRC + DEST * (1-SRC_ALPHA) + * + * By contrast, non pre-multiplied surfaces use the following equation: + * + * DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA) + * + * pre-multiplied surfaces must always be used if transparent pixels are + * composited on top of each-other into the surface. A pre-multiplied + * surface can never lower the value of the alpha component of a given + * pixel. + * + * In some rare situations, a non pre-multiplied surface is preferable. + * + */ + public static final int NON_PREMULTIPLIED = 0x00000100; + + /** + * Surface creation flag: Indicates that the surface must be considered opaque, + * even if its pixel format is set to translucent. This can be useful if an + * application needs full RGBA 8888 support for instance but will + * still draw every pixel opaque. + * + */ + public static final int OPAQUE = 0x00000400; + + /** + * Surface creation flag: Application requires a hardware-protected path to an + * external display sink. If a hardware-protected path is not available, + * then this surface will not be displayed on the external sink. + * + */ + public static final int PROTECTED_APP = 0x00000800; + + // 0x1000 is reserved for an independent DRM protected flag in framework + + /** + * Surface creation flag: Creates a normal surface. + * This is the default. + * + */ + public static final int FX_SURFACE_NORMAL = 0x00000000; + + /** + * Surface creation flag: Creates a Blur surface. + * Everything behind this surface is blurred by some amount. + * The quality and refresh speed of the blur effect is not settable or guaranteed. + * It is an error to lock a Blur surface, since it doesn't have a backing store. + * + * @deprecated + */ + @Deprecated + public static final int FX_SURFACE_BLUR = 0x00010000; + + /** + * Surface creation flag: Creates a Dim surface. + * Everything behind this surface is dimmed by the amount specified + * in {@link #setAlpha}. It is an error to lock a Dim surface, since it + * doesn't have a backing store. + * + */ + public static final int FX_SURFACE_DIM = 0x00020000; + + /** + * + */ + public static final int FX_SURFACE_SCREENSHOT = 0x00030000; + + /** + * Mask used for FX values above. + * + */ + public static final int FX_SURFACE_MASK = 0x000F0000; + + /* flags used with setFlags() (keep in sync with ISurfaceComposer.h) */ + + /** + * Surface flag: Hide the surface. + * Equivalent to calling hide(). + */ + public static final int SURFACE_HIDDEN = 0x01; + + + /* built-in physical display ids (keep in sync with ISurfaceComposer.h) + * these are different from the logical display ids used elsewhere in the framework */ + + /** + * Built-in physical display id: Main display. + * Use only with {@link SurfaceControl#getBuiltInDisplay()}. + */ + public static final int BUILT_IN_DISPLAY_ID_MAIN = 0; + + /** + * Built-in physical display id: Attached HDMI display. + * Use only with {@link SurfaceControl#getBuiltInDisplay()}. + */ + public static final int BUILT_IN_DISPLAY_ID_HDMI = 1; + + + + /** + * Create a surface with a name. + * + * The surface creation flags specify what kind of surface to create and + * certain options such as whether the surface can be assumed to be opaque + * and whether it should be initially hidden. Surfaces should always be + * created with the {@link #HIDDEN} flag set to ensure that they are not + * made visible prematurely before all of the surface's properties have been + * configured. + * + * Good practice is to first create the surface with the {@link #HIDDEN} flag + * specified, open a transaction, set the surface layer, layer stack, alpha, + * and position, call {@link #show} if appropriate, and close the transaction. + * + * @param session The surface session, must not be null. + * @param name The surface name, must not be null. + * @param w The surface initial width. + * @param h The surface initial height. + * @param flags The surface creation flags. Should always include {@link #HIDDEN} + * in the creation flags. + */ + public SurfaceControl(SurfaceSession session, + String name, int w, int h, int format, int flags) + throws OutOfResourcesException { + if (session == null) { + throw new IllegalArgumentException("session must not be null"); + } + if (name == null) { + throw new IllegalArgumentException("name must not be null"); + } + + if ((flags & SurfaceControl.HIDDEN) == 0) { + Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set " + + "to ensure that they are not made visible prematurely before " + + "all of the surface's properties have been configured. " + + "Set the other properties and make the surface visible within " + + "a transaction. New surface name: " + name, + new Throwable()); + } + + checkHeadless(); + + mName = name; + mNativeObject = nativeCreate(session, name, w, h, format, flags); + if (mNativeObject == 0) { + throw new OutOfResourcesException( + "Couldn't allocate SurfaceControl native object"); + } + + mCloseGuard.open("release"); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + } + } finally { + super.finalize(); + } + } + + @Override + public String toString() { + return "Surface(name=" + mName + ")"; + } + + /** + * Release the local reference to the server-side surface. + * Always call release() when you're done with a Surface. + * This will make the surface invalid. + */ + public void release() { + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + mNativeObject = 0; + } + mCloseGuard.close(); + } + + /** + * Free all server-side state associated with this surface and + * release this object's reference. This method can only be + * called from the process that created the service. + */ + public void destroy() { + if (mNativeObject != 0) { + nativeDestroy(mNativeObject); + mNativeObject = 0; + } + mCloseGuard.close(); + } + + private void checkNotReleased() { + if (mNativeObject == 0) throw new NullPointerException( + "mNativeObject is null. Have you called release() already?"); + } + + /* + * set surface parameters. + * needs to be inside open/closeTransaction block + */ + + /** start a transaction */ + public static void openTransaction() { + nativeOpenTransaction(); + } + + /** end a transaction */ + public static void closeTransaction() { + nativeCloseTransaction(); + } + + /** flag the transaction as an animation */ + public static void setAnimationTransaction() { + nativeSetAnimationTransaction(); + } + + public void setLayer(int zorder) { + checkNotReleased(); + nativeSetLayer(mNativeObject, zorder); + } + + public void setPosition(int x, int y) { + checkNotReleased(); + nativeSetPosition(mNativeObject, (float)x, (float)y); + } + + public void setPosition(float x, float y) { + checkNotReleased(); + nativeSetPosition(mNativeObject, x, y); + } + + public void setSize(int w, int h) { + checkNotReleased(); + nativeSetSize(mNativeObject, w, h); + } + + public void hide() { + checkNotReleased(); + nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN); + } + + public void show() { + checkNotReleased(); + nativeSetFlags(mNativeObject, 0, SURFACE_HIDDEN); + } + + public void setTransparentRegionHint(Region region) { + checkNotReleased(); + nativeSetTransparentRegionHint(mNativeObject, region); + } + + public void setAlpha(float alpha) { + checkNotReleased(); + nativeSetAlpha(mNativeObject, alpha); + } + + public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + checkNotReleased(); + nativeSetMatrix(mNativeObject, dsdx, dtdx, dsdy, dtdy); + } + + public void setFlags(int flags, int mask) { + checkNotReleased(); + nativeSetFlags(mNativeObject, flags, mask); + } + + public void setWindowCrop(Rect crop) { + checkNotReleased(); + if (crop != null) { + nativeSetWindowCrop(mNativeObject, + crop.left, crop.top, crop.right, crop.bottom); + } else { + nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0); + } + } + + public void setLayerStack(int layerStack) { + checkNotReleased(); + nativeSetLayerStack(mNativeObject, layerStack); + } + + /* + * set display parameters. + * needs to be inside open/closeTransaction block + */ + + /** + * Describes the properties of a physical display known to surface flinger. + */ + public static final class PhysicalDisplayInfo { + public int width; + public int height; + public float refreshRate; + public float density; + public float xDpi; + public float yDpi; + public boolean secure; + + public PhysicalDisplayInfo() { + } + + public PhysicalDisplayInfo(PhysicalDisplayInfo other) { + copyFrom(other); + } + + @Override + public boolean equals(Object o) { + return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o); + } + + public boolean equals(PhysicalDisplayInfo other) { + return other != null + && width == other.width + && height == other.height + && refreshRate == other.refreshRate + && density == other.density + && xDpi == other.xDpi + && yDpi == other.yDpi + && secure == other.secure; + } + + @Override + public int hashCode() { + return 0; // don't care + } + + public void copyFrom(PhysicalDisplayInfo other) { + width = other.width; + height = other.height; + refreshRate = other.refreshRate; + density = other.density; + xDpi = other.xDpi; + yDpi = other.yDpi; + secure = other.secure; + } + + // For debugging purposes + @Override + public String toString() { + return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, " + + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure + + "}"; + } + } + + public static void unblankDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeUnblankDisplay(displayToken); + } + + public static void blankDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeBlankDisplay(displayToken); + } + + public static boolean getDisplayInfo(IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + if (outInfo == null) { + throw new IllegalArgumentException("outInfo must not be null"); + } + return nativeGetDisplayInfo(displayToken, outInfo); + } + + public static void setDisplayProjection(IBinder displayToken, + int orientation, Rect layerStackRect, Rect displayRect) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + if (layerStackRect == null) { + throw new IllegalArgumentException("layerStackRect must not be null"); + } + if (displayRect == null) { + throw new IllegalArgumentException("displayRect must not be null"); + } + nativeSetDisplayProjection(displayToken, orientation, + layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom, + displayRect.left, displayRect.top, displayRect.right, displayRect.bottom); + } + + public static void setDisplayLayerStack(IBinder displayToken, int layerStack) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeSetDisplayLayerStack(displayToken, layerStack); + } + + public static void setDisplaySurface(IBinder displayToken, Surface surface) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + if (surface == null) { + throw new IllegalArgumentException("surface must not be null"); + } + if (surface.mNativeObject == 0) + throw new NullPointerException("Surface native object is null. Are you using a released surface?"); + + nativeSetDisplaySurface(displayToken, surface.mNativeObject); + } + + public static IBinder createDisplay(String name, boolean secure) { + if (name == null) { + throw new IllegalArgumentException("name must not be null"); + } + return nativeCreateDisplay(name, secure); + } + + public static IBinder getBuiltInDisplay(int builtInDisplayId) { + return nativeGetBuiltInDisplay(builtInDisplayId); + } + + + /** + * Copy the current screen contents into a bitmap and return it. + * + * @param width The desired width of the returned bitmap; the raw + * screen will be scaled down to this size. + * @param height The desired height of the returned bitmap; the raw + * screen will be scaled down to this size. + * @param minLayer The lowest (bottom-most Z order) surface layer to + * include in the screenshot. + * @param maxLayer The highest (top-most Z order) surface layer to + * include in the screenshot. + * @return Returns a Bitmap containing the screen contents, or null + * if an error occurs. + * + */ + public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) { + // TODO: should take the display as a parameter + IBinder displayToken = SurfaceControl.getBuiltInDisplay(SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); + return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false); + } + + /** + * Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all + * Surfaces in the screenshot. + * + */ + public static Bitmap screenshot(int width, int height) { + // TODO: should take the display as a parameter + IBinder displayToken = SurfaceControl.getBuiltInDisplay(SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); + return nativeScreenshot(displayToken, width, height, 0, 0, true); + } + + private static void checkHeadless() { + if (HEADLESS) { + throw new UnsupportedOperationException("Device is headless"); + } + } +} diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 82b3963..eb81f72 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -16,10 +16,7 @@ package android.view; -import android.util.Poolable; -import android.util.Pool; -import android.util.Pools; -import android.util.PoolableManager; +import android.util.Pools.SynchronizedPool; /** * Helper for tracking the velocity of touch events, for implementing @@ -31,30 +28,15 @@ import android.util.PoolableManager; * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)} * and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id. */ -public final class VelocityTracker implements Poolable<VelocityTracker> { - private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( - Pools.finitePool(new PoolableManager<VelocityTracker>() { - public VelocityTracker newInstance() { - return new VelocityTracker(null); - } - - public void onAcquired(VelocityTracker element) { - // Intentionally empty - } - - public void onReleased(VelocityTracker element) { - element.clear(); - } - }, 2)); +public final class VelocityTracker { + private static final SynchronizedPool<VelocityTracker> sPool = + new SynchronizedPool<VelocityTracker>(2); private static final int ACTIVE_POINTER_ID = -1; private int mPtr; private final String mStrategy; - private VelocityTracker mNext; - private boolean mIsPooled; - private static native int nativeInitialize(String strategy); private static native void nativeDispose(int ptr); private static native void nativeClear(int ptr); @@ -73,7 +55,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return Returns a new VelocityTracker. */ static public VelocityTracker obtain() { - return sPool.acquire(); + VelocityTracker instance = sPool.acquire(); + return (instance != null) ? instance : new VelocityTracker(null); } /** @@ -98,38 +81,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { */ public void recycle() { if (mStrategy == null) { + clear(); sPool.release(this); } } - /** - * @hide - */ - public void setNextPoolable(VelocityTracker element) { - mNext = element; - } - - /** - * @hide - */ - public VelocityTracker getNextPoolable() { - return mNext; - } - - /** - * @hide - */ - public boolean isPooled() { - return mIsPooled; - } - - /** - * @hide - */ - public void setPooled(boolean isPooled) { - mIsPooled = isPooled; - } - private VelocityTracker(String strategy) { mPtr = nativeInitialize(strategy); mStrategy = strategy; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0d2141f..dcf51e4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,6 +40,7 @@ import android.graphics.Shader; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -52,10 +53,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; -import android.util.Pool; -import android.util.Poolable; -import android.util.PoolableManager; -import android.util.Pools; +import android.util.Pools.SynchronizedPool; import android.util.Property; import android.util.SparseArray; import android.util.TypedValue; @@ -691,6 +689,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int NO_ID = -1; + private static boolean sUseBrokenMakeMeasureSpec = false; + /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. @@ -1561,9 +1561,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ int mAccessibilityViewId = NO_ID; - /** - * @hide - */ private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; /** @@ -1870,6 +1867,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT; /** + * Default horizontal layout direction. + * @hide + */ + static final int LAYOUT_DIRECTION_RESOLVED_DEFAULT = LAYOUT_DIRECTION_LTR; + + /** * Indicates that the view is tracking some sort of transient state * that the app should not need to be aware of, but that the framework * should take special care to preserve. @@ -1918,6 +1921,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT; /** + * Default resolved text direction + * @hide + */ + static final int TEXT_DIRECTION_RESOLVED_DEFAULT = TEXT_DIRECTION_FIRST_STRONG; + + /** * Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED) * @hide */ @@ -1969,7 +1978,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ static final int PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT = - TEXT_DIRECTION_FIRST_STRONG << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; + TEXT_DIRECTION_RESOLVED_DEFAULT << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; /* * Default text alignment. The text alignment of this View is inherited from its parent. @@ -2028,6 +2037,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY; /** + * Default resolved text alignment + * @hide + */ + static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = TEXT_ALIGNMENT_GRAVITY; + + /** * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED) * @hide */ @@ -2077,7 +2092,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Indicates whether if the view text alignment has been resolved to gravity */ private static final int PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT = - TEXT_ALIGNMENT_GRAVITY << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; + TEXT_ALIGNMENT_RESOLVED_DEFAULT << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; // Accessiblity constants for mPrivateFlags2 @@ -2515,8 +2530,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * The undefined cursor position. + * + * @hide */ - private static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1; + public static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1; /** * Indicates that the screen has changed state and is now off. @@ -3237,6 +3254,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; + + if (!sUseBrokenMakeMeasureSpec && context.getApplicationInfo().targetSdkVersion <= + Build.VERSION_CODES.JELLY_BEAN_MR1 ) { + // Older apps may need this compatibility hack for measurement. + sUseBrokenMakeMeasureSpec = true; + } } /** @@ -4370,10 +4393,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & PFLAG_FOCUSED) == 0) { mPrivateFlags |= PFLAG_FOCUSED; + View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null; + if (mParent != null) { mParent.requestChildFocus(this, this); } + if (mAttachInfo != null) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this); + } + onFocusChanged(true, direction, previouslyFocusedRect); refreshDrawableState(); @@ -4480,7 +4509,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, refreshDrawableState(); - ensureInputFocusOnFirstFocusable(); + if (!rootViewRequestFocus()) { + notifyGlobalFocusCleared(this); + } if (AccessibilityManager.getInstance(mContext).isEnabled()) { notifyAccessibilityStateChanged(); @@ -4488,11 +4519,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - void ensureInputFocusOnFirstFocusable() { + void notifyGlobalFocusCleared(View oldFocus) { + if (oldFocus != null && mAttachInfo != null) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + } + } + + boolean rootViewRequestFocus() { View root = getRootView(); if (root != null) { - root.requestFocus(); + return root.requestFocus(); } + return false; } /** @@ -4838,13 +4876,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, event.setEnabled(isEnabled()); event.setContentDescription(mContentDescription); - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) { - ArrayList<View> focusablesTempList = mAttachInfo.mTempArrayList; - getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, - FOCUSABLES_ALL); - event.setItemCount(focusablesTempList.size()); - event.setCurrentItemIndex(focusablesTempList.indexOf(this)); - focusablesTempList.clear(); + switch (event.getEventType()) { + case AccessibilityEvent.TYPE_VIEW_FOCUSED: { + ArrayList<View> focusablesTempList = (mAttachInfo != null) + ? mAttachInfo.mTempArrayList : new ArrayList<View>(); + getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL); + event.setItemCount(focusablesTempList.size()); + event.setCurrentItemIndex(focusablesTempList.indexOf(this)); + if (mAttachInfo != null) { + focusablesTempList.clear(); + } + } break; + case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: { + CharSequence text = getIterableTextForAccessibility(); + if (text != null && text.length() > 0) { + event.setFromIndex(getAccessibilitySelectionStart()); + event.setToIndex(getAccessibilitySelectionEnd()); + event.setItemCount(text.length()); + } + } break; } } @@ -4986,6 +5036,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (label != null) { info.setLabeledBy(label); } + + if ((mAttachInfo.mAccessibilityFetchFlags + & AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS) != 0) { + try { + String viewId = getResources().getResourceName(mID); + info.setViewIdResourceName(viewId); + } catch (Resources.NotFoundException nfe) { + /* ignore */ + } + } } if (mLabelForId != View.NO_ID) { @@ -5041,7 +5101,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); } - if (mContentDescription != null && mContentDescription.length() > 0) { + CharSequence text = getIterableTextForAccessibility(); + if (text != null && text.length() > 0) { + info.setTextSelection(getAccessibilitySelectionStart(), getAccessibilitySelectionEnd()); + + info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION); info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER @@ -5923,7 +5987,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; if (targetSdkVersion < JELLY_BEAN_MR1) { mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; - return LAYOUT_DIRECTION_LTR; + return LAYOUT_DIRECTION_RESOLVED_DEFAULT; } return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) == PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; @@ -6817,7 +6881,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public boolean includeForAccessibility() { if (mAttachInfo != null) { - return mAttachInfo.mIncludeNotImportantViews || isImportantForAccessibility(); + return (mAttachInfo.mAccessibilityFetchFlags + & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0 + || isImportantForAccessibility(); } return false; } @@ -6966,21 +7032,43 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (arguments != null) { final int granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); - return nextAtGranularity(granularity); + final boolean extendSelection = arguments.getBoolean( + AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN); + return nextAtGranularity(granularity, extendSelection); } } break; case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: { if (arguments != null) { final int granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); - return previousAtGranularity(granularity); + final boolean extendSelection = arguments.getBoolean( + AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN); + return previousAtGranularity(granularity, extendSelection); + } + } break; + case AccessibilityNodeInfo.ACTION_SET_SELECTION: { + CharSequence text = getIterableTextForAccessibility(); + if (text == null) { + return false; + } + final int start = (arguments != null) ? arguments.getInt( + AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1; + final int end = (arguments != null) ? arguments.getInt( + AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1; + // Only cursor position can be specified (selection length == 0) + if ((getAccessibilitySelectionStart() != start + || getAccessibilitySelectionEnd() != end) + && (start == end)) { + setAccessibilitySelection(start, end); + notifyAccessibilityStateChanged(); + return true; } } break; } return false; } - private boolean nextAtGranularity(int granularity) { + private boolean nextAtGranularity(int granularity, boolean extendSelection) { CharSequence text = getIterableTextForAccessibility(); if (text == null || text.length() == 0) { return false; @@ -6989,21 +7077,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (iterator == null) { return false; } - final int current = getAccessibilityCursorPosition(); + int current = getAccessibilitySelectionEnd(); + if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) { + current = 0; + } final int[] range = iterator.following(current); if (range == null) { return false; } final int start = range[0]; final int end = range[1]; - setAccessibilityCursorPosition(end); + if (extendSelection && isAccessibilitySelectionExtendable()) { + int selectionStart = getAccessibilitySelectionStart(); + if (selectionStart == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) { + selectionStart = start; + } + setAccessibilitySelection(selectionStart, end); + } else { + setAccessibilitySelection(end, end); + } sendViewTextTraversedAtGranularityEvent( AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, granularity, start, end); return true; } - private boolean previousAtGranularity(int granularity) { + private boolean previousAtGranularity(int granularity, boolean extendSelection) { CharSequence text = getIterableTextForAccessibility(); if (text == null || text.length() == 0) { return false; @@ -7012,15 +7111,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (iterator == null) { return false; } - int current = getAccessibilityCursorPosition(); + int current = getAccessibilitySelectionStart(); if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) { current = text.length(); - setAccessibilityCursorPosition(current); - } else if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) { - // When traversing by character we always put the cursor after the character - // to ease edit and have to compensate before asking the for previous segment. - current--; - setAccessibilityCursorPosition(current); } final int[] range = iterator.preceding(current); if (range == null) { @@ -7028,11 +7121,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } final int start = range[0]; final int end = range[1]; - // Always put the cursor after the character to ease edit. - if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) { - setAccessibilityCursorPosition(end); + if (extendSelection && isAccessibilitySelectionExtendable()) { + int selectionEnd = getAccessibilitySelectionEnd(); + if (selectionEnd == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) { + selectionEnd = end; + } + setAccessibilitySelection(start, selectionEnd); } else { - setAccessibilityCursorPosition(start); + setAccessibilitySelection(start, start); } sendViewTextTraversedAtGranularityEvent( AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, @@ -7052,17 +7148,43 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Gets whether accessibility selection can be extended. + * + * @return If selection is extensible. + * * @hide */ - public int getAccessibilityCursorPosition() { + public boolean isAccessibilitySelectionExtendable() { + return false; + } + + /** + * @hide + */ + public int getAccessibilitySelectionStart() { return mAccessibilityCursorPosition; } /** * @hide */ - public void setAccessibilityCursorPosition(int position) { - mAccessibilityCursorPosition = position; + public int getAccessibilitySelectionEnd() { + return getAccessibilitySelectionStart(); + } + + /** + * @hide + */ + public void setAccessibilitySelection(int start, int end) { + if (start == end && end == mAccessibilityCursorPosition) { + return; + } + if (start >= 0 && start == end && end <= getIterableTextForAccessibility().length()) { + mAccessibilityCursorPosition = start; + } else { + mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; + } + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); } private void sendViewTextTraversedAtGranularityEvent(int action, int granularity, @@ -9806,8 +9928,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, outRect.set(mLeft, mTop, mRight, mBottom); } else { final RectF tmpRect = mAttachInfo.mTmpTransformRect; - tmpRect.set(-info.mPivotX, -info.mPivotY, - getWidth() - info.mPivotX, getHeight() - info.mPivotY); + tmpRect.set(0, 0, getWidth(), getHeight()); info.mMatrix.mapRect(tmpRect); outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop, (int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop); @@ -9928,7 +10049,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTop += offset; mBottom += offset; if (mDisplayList != null) { - mDisplayList.offsetTopBottom(offset); + mDisplayList.offsetTopAndBottom(offset); invalidateViewProperty(false, false); } else { if (!matrixIsIdentity) { @@ -9976,7 +10097,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLeft += offset; mRight += offset; if (mDisplayList != null) { - mDisplayList.offsetLeftRight(offset); + mDisplayList.offsetLeftAndRight(offset); invalidateViewProperty(false, false); } else { if (!matrixIsIdentity) { @@ -10749,7 +10870,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire(); + final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.obtain(); info.target = this; info.left = left; info.top = top; @@ -10798,7 +10919,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire(); + final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.obtain(); info.target = this; info.left = left; info.top = top; @@ -11564,8 +11685,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, imm.focusIn(this); } - if (mAttachInfo != null && mDisplayList != null) { - mAttachInfo.mViewRootImpl.dequeueDisplayList(mDisplayList); + if (mDisplayList != null) { + mDisplayList.clearDirty(); } } @@ -11691,11 +11812,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // later to get the correct resolved value if (!canResolveLayoutDirection()) return false; - View parent = ((View) mParent); // Parent has not yet resolved, LTR is still the default - if (!parent.isLayoutDirectionResolved()) return false; + if (!mParent.isLayoutDirectionResolved()) return false; - if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { + if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; } break; @@ -11728,8 +11848,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public boolean canResolveLayoutDirection() { switch (getRawLayoutDirection()) { case LAYOUT_DIRECTION_INHERIT: - return (mParent != null) && (mParent instanceof ViewGroup) && - ((ViewGroup) mParent).canResolveLayoutDirection(); + return (mParent != null) && mParent.canResolveLayoutDirection(); default: return true; } @@ -11757,8 +11876,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if layout direction has been resolved. + * @hide */ - private boolean isLayoutDirectionResolved() { + public boolean isLayoutDirectionResolved() { return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED; } @@ -11845,6 +11965,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mAttachInfo != null) { if (mDisplayList != null) { + mDisplayList.markDirty(); mAttachInfo.mViewRootImpl.enqueueDisplayList(mDisplayList); } mAttachInfo.mViewRootImpl.cancelInvalidate(this); @@ -12548,8 +12669,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * @return The HardwareRenderer associated with that view or null if hardware rendering - * is not supported or this this has not been attached to a window. + * @return The {@link HardwareRenderer} associated with that view or null if + * hardware rendering is not supported or this view is not attached + * to a window. * * @hide */ @@ -12604,15 +12726,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } boolean caching = false; - final HardwareCanvas canvas = displayList.start(); int width = mRight - mLeft; int height = mBottom - mTop; + int layerType = getLayerType(); + + final HardwareCanvas canvas = displayList.start(width, height); try { - canvas.setViewport(width, height); - // The dirty rect should always be null for a display list - canvas.onPreDraw(null); - int layerType = getLayerType(); if (!isLayer && layerType != LAYER_TYPE_NONE) { if (layerType == LAYER_TYPE_HARDWARE) { final HardwareLayer layer = getHardwareLayer(); @@ -12650,8 +12770,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } finally { - canvas.onPostDraw(); - displayList.end(); displayList.setCaching(caching); if (isLayer) { @@ -12696,7 +12814,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void clearDisplayList() { if (mDisplayList != null) { - mDisplayList.invalidate(); mDisplayList.clear(); } } @@ -13272,7 +13389,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, alpha = transform.getAlpha(); } if ((transformType & Transformation.TYPE_MATRIX) != 0) { - displayList.setStaticMatrix(transform.getMatrix()); + displayList.setMatrix(transform.getMatrix()); } } } @@ -13347,8 +13464,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } transformToApply = parent.mChildTransformation; } else { - if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) == PFLAG3_VIEW_IS_ANIMATING_TRANSFORM && - mDisplayList != null) { + if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) == + PFLAG3_VIEW_IS_ANIMATING_TRANSFORM && mDisplayList != null) { // No longer animating: clear out old animation matrix mDisplayList.setAnimationMatrix(null); mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; @@ -13545,7 +13662,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN && - !useDisplayListProperties) { + !useDisplayListProperties && layerType == LAYER_TYPE_NONE) { if (offsetForScroll) { canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop)); } else { @@ -13977,6 +14094,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Return true if o is a ViewGroup that is laying out using optical bounds. + * @hide + */ + public static boolean isLayoutModeOptical(Object o) { + return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical(); + } + + private boolean setOpticalFrame(int left, int top, int right, int bottom) { + Insets parentInsets = mParent instanceof View ? + ((View) mParent).getOpticalInsets() : Insets.NONE; + Insets childInsets = getOpticalInsets(); + return setFrame( + left + parentInsets.left - childInsets.left, + top + parentInsets.top - childInsets.top, + right + parentInsets.left + childInsets.right, + bottom + parentInsets.top + childInsets.bottom); + } + + /** * Assign a size and position to a view and all of its * descendants * @@ -14002,7 +14138,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int oldT = mTop; int oldB = mBottom; int oldR = mRight; - boolean changed = setFrame(l, t, r, b); + boolean changed = isLayoutModeOptical(mParent) ? + setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; @@ -14444,6 +14581,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mBackground instanceof ColorDrawable) { ((ColorDrawable) mBackground.mutate()).setColor(color); computeOpaqueFlags(); + mBackgroundResource = 0; } else { setBackground(new ColorDrawable(color)); } @@ -14818,6 +14956,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (mUserPaddingStart != UNDEFINED_PADDING || mUserPaddingEnd != UNDEFINED_PADDING); } + Insets computeOpticalInsets() { + return (mBackground == null) ? Insets.NONE : mBackground.getOpticalInsets(); + } + /** * @hide */ @@ -14841,19 +14983,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public Insets getOpticalInsets() { if (mLayoutInsets == null) { - mLayoutInsets = (mBackground == null) ? Insets.NONE : mBackground.getLayoutInsets(); + mLayoutInsets = computeOpticalInsets(); } return mLayoutInsets; } /** - * @hide - */ - public void setLayoutInsets(Insets layoutInsets) { - mLayoutInsets = layoutInsets; - } - - /** * Changes the selection state of this view. A view can be selected or not. * Note that selection is not the same as focus. Views are typically * selected in the context of an AdapterView like ListView or GridView; @@ -15460,17 +15595,50 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Returns whether the view hierarchy is currently undergoing a layout pass. This + * information is useful to avoid situations such as calling {@link #requestLayout()} during + * a layout pass. + * + * @return whether the view hierarchy is currently undergoing a layout pass + */ + public boolean isInLayout() { + ViewRootImpl viewRoot = getViewRootImpl(); + return (viewRoot != null && viewRoot.isInLayout()); + } + + /** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view - * tree. + * tree. This should not be called while the view hierarchy is currently in a layout + * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the + * end of the current layout pass (and then layout will run again) or after the current + * frame is drawn and the next layout occurs. + * + * <p>Subclasses which override this method should call the superclass method to + * handle possible request-during-layout errors correctly.</p> */ public void requestLayout() { + if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { + // Only trigger request-during-layout logic if this is the view requesting it, + // not the views in its parent hierarchy + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot != null && viewRoot.isInLayout()) { + if (!viewRoot.requestLayoutDuringLayout(this)) { + return; + } + } + mAttachInfo.mViewRequestingLayout = this; + } + mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } + if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { + mAttachInfo.mViewRequestingLayout = null; + } } /** @@ -15504,6 +15672,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #onMeasure(int, int) */ public final void measure(int widthMeasureSpec, int heightMeasureSpec) { + boolean optical = isLayoutModeOptical(this); + if (optical != isLayoutModeOptical(mParent)) { + Insets insets = getOpticalInsets(); + int oWidth = insets.left + insets.right; + int oHeight = insets.top + insets.bottom; + widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); + heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); + } if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { @@ -15595,6 +15771,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #MEASURED_STATE_TOO_SMALL}. */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { + boolean optical = isLayoutModeOptical(this); + if (optical != isLayoutModeOptical(mParent)) { + Insets insets = getOpticalInsets(); + int opticalWidth = insets.left + insets.right; + int opticalHeight = insets.top + insets.bottom; + + measuredWidth += optical ? opticalWidth : -opticalWidth; + measuredHeight += optical ? opticalHeight : -opticalHeight; + } mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; @@ -16722,16 +16907,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } - View parent = ((View) mParent); // Parent has not yet resolved, so we still return the default - if (!parent.isTextDirectionResolved()) { + if (!mParent.isTextDirectionResolved()) { mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; // Resolution will need to happen again later return false; } // Set current resolved direction to the same value as the parent's one - final int parentResolvedDirection = parent.getTextDirection(); + final int parentResolvedDirection = mParent.getTextDirection(); switch (parentResolvedDirection) { case TEXT_DIRECTION_FIRST_STRONG: case TEXT_DIRECTION_ANY_RTL: @@ -16772,12 +16956,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Check if text direction resolution can be done. * * @return true if text direction resolution can be done otherwise return false. + * + * @hide */ - private boolean canResolveTextDirection() { + public boolean canResolveTextDirection() { switch (getRawTextDirection()) { case TEXT_DIRECTION_INHERIT: - return (mParent != null) && (mParent instanceof View) && - ((View) mParent).canResolveTextDirection(); + return (mParent != null) && mParent.canResolveTextDirection(); default: return true; } @@ -16807,8 +16992,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if text direction is resolved. + * + * @hide */ - private boolean isTextDirectionResolved() { + public boolean isTextDirectionResolved() { return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED) == PFLAG2_TEXT_DIRECTION_RESOLVED; } @@ -16931,16 +17118,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Resolution will need to happen again later return false; } - View parent = (View) mParent; // Parent has not yet resolved, so we still return the default - if (!parent.isTextAlignmentResolved()) { + if (!mParent.isTextAlignmentResolved()) { mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; // Resolution will need to happen again later return false; } - final int parentResolvedTextAlignment = parent.getTextAlignment(); + final int parentResolvedTextAlignment = mParent.getTextAlignment(); switch (parentResolvedTextAlignment) { case TEXT_ALIGNMENT_GRAVITY: case TEXT_ALIGNMENT_TEXT_START: @@ -16985,12 +17171,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Check if text alignment resolution can be done. * * @return true if text alignment resolution can be done otherwise return false. + * + * @hide */ - private boolean canResolveTextAlignment() { + public boolean canResolveTextAlignment() { switch (getRawTextAlignment()) { case TEXT_DIRECTION_INHERIT: - return (mParent != null) && (mParent instanceof View) && - ((View) mParent).canResolveTextAlignment(); + return (mParent != null) && mParent.canResolveTextAlignment(); default: return true; } @@ -17020,8 +17207,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if text alignment is resolved. + * + * @hide */ - private boolean isTextAlignmentResolved() { + public boolean isTextAlignmentResolved() { return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED) == PFLAG2_TEXT_ALIGNMENT_RESOLVED; } @@ -17266,12 +17455,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> * </ul> * + * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's + * implementation was such that the order of arguments did not matter + * and overflow in either value could impact the resulting MeasureSpec. + * {@link android.widget.RelativeLayout} was affected by this bug. + * Apps targeting API levels greater than 17 will get the fixed, more strict + * behavior.</p> + * * @param size the size of the measure specification * @param mode the mode of the measure specification * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { - return size + mode; + if (sUseBrokenMakeMeasureSpec) { + return size + mode; + } else { + return (size & ~MODE_MASK) | (mode & MODE_MASK); + } } /** @@ -17296,6 +17496,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (measureSpec & ~MODE_MASK); } + static int adjust(int measureSpec, int delta) { + return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); + } + /** * Returns a String representation of the specified measure * specification. @@ -17633,25 +17837,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * POOL_LIMIT objects that get reused. This reduces memory allocations * whenever possible. */ - static class InvalidateInfo implements Poolable<InvalidateInfo> { + static class InvalidateInfo { private static final int POOL_LIMIT = 10; - private static final Pool<InvalidateInfo> sPool = Pools.synchronizedPool( - Pools.finitePool(new PoolableManager<InvalidateInfo>() { - public InvalidateInfo newInstance() { - return new InvalidateInfo(); - } - - public void onAcquired(InvalidateInfo element) { - } - - public void onReleased(InvalidateInfo element) { - element.target = null; - } - }, POOL_LIMIT) - ); - private InvalidateInfo mNext; - private boolean mIsPooled; + private static final SynchronizedPool<InvalidateInfo> sPool = + new SynchronizedPool<InvalidateInfo>(POOL_LIMIT); View target; @@ -17660,29 +17850,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int right; int bottom; - public void setNextPoolable(InvalidateInfo element) { - mNext = element; - } - - public InvalidateInfo getNextPoolable() { - return mNext; - } - - static InvalidateInfo acquire() { - return sPool.acquire(); + public static InvalidateInfo obtain() { + InvalidateInfo instance = sPool.acquire(); + return (instance != null) ? instance : new InvalidateInfo(); } - void release() { + public void recycle() { + target = null; sPool.release(this); } - - public boolean isPooled() { - return mIsPooled; - } - - public void setPooled(boolean isPooled) { - mIsPooled = isPooled; - } } final IWindowSession mSession; @@ -17926,10 +18102,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mAccessibilityWindowId = View.NO_ID; /** - * Whether to ingore not exposed for accessibility Views when - * reporting the view tree to accessibility services. + * Flags related to accessibility processing. + * + * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS + * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS */ - boolean mIncludeNotImportantViews; + int mAccessibilityFetchFlags; /** * The drawable for highlighting accessibility focus. @@ -17947,6 +18125,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Point mPoint = new Point(); /** + * Used to track which View originated a requestLayout() call, used when + * requestLayout() is called during layout. + */ + View mViewRequestingLayout; + + /** * Creates a new set of attachment information with the specified * events handler and thread. * diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 023e58f..987ff78 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. @@ -406,7 +407,7 @@ public class ViewDebug { view = view.getRootView(); if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) { - dump(view, clientStream); + dump(view, false, true, clientStream); } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) { captureLayers(view, new DataOutputStream(clientStream)); } else { @@ -425,7 +426,8 @@ public class ViewDebug { } } - private static View findView(View root, String parameter) { + /** @hide */ + public static View findView(View root, String parameter) { // Look by type/hashcode if (parameter.indexOf('@') != -1) { final String[] ids = parameter.split("@"); @@ -488,7 +490,8 @@ public class ViewDebug { } } - private static void profileViewAndChildren(final View view, BufferedWriter out) + /** @hide */ + public static void profileViewAndChildren(final View view, BufferedWriter out) throws IOException { profileViewAndChildren(view, out, true); } @@ -623,7 +626,8 @@ public class ViewDebug { return duration[0]; } - private static void captureLayers(View root, final DataOutputStream clientStream) + /** @hide */ + public static void captureLayers(View root, final DataOutputStream clientStream) throws IOException { try { @@ -695,10 +699,21 @@ public class ViewDebug { view.getViewRootImpl().outputDisplayList(view); } + /** @hide */ + public static void outputDisplayList(View root, View target) { + root.getViewRootImpl().outputDisplayList(target); + } + private static void capture(View root, final OutputStream clientStream, String parameter) throws IOException { final View captureView = findView(root, parameter); + capture(root, clientStream, captureView); + } + + /** @hide */ + public static void capture(View root, final OutputStream clientStream, View captureView) + throws IOException { Bitmap b = performViewCapture(captureView, false); if (b == null) { @@ -752,14 +767,20 @@ public class ViewDebug { return null; } - private static void dump(View root, OutputStream clientStream) throws IOException { + /** + * Dumps the view hierarchy starting from the given view. + * @hide + */ + public static void dump(View root, boolean skipChildren, boolean includeProperties, + OutputStream clientStream) throws IOException { BufferedWriter out = null; try { out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024); View view = root.getRootView(); if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; - dumpViewHierarchyWithProperties(group.getContext(), group, out, 0); + dumpViewHierarchy(group.getContext(), group, out, 0, + skipChildren, includeProperties); } out.write("DONE."); out.newLine(); @@ -804,9 +825,13 @@ public class ViewDebug { return view.getClass().getName().equals(className) && view.hashCode() == hashCode; } - private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group, - BufferedWriter out, int level) { - if (!dumpViewWithProperties(context, group, out, level)) { + private static void dumpViewHierarchy(Context context, ViewGroup group, + BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) { + if (!dumpView(context, group, out, level, includeProperties)) { + return; + } + + if (skipChildren) { return; } @@ -814,9 +839,10 @@ public class ViewDebug { for (int i = 0; i < count; i++) { final View view = group.getChildAt(i); if (view instanceof ViewGroup) { - dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1); + dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren, + includeProperties); } else { - dumpViewWithProperties(context, view, out, level + 1); + dumpView(context, view, out, level + 1, includeProperties); } } if (group instanceof HierarchyHandler) { @@ -824,8 +850,8 @@ public class ViewDebug { } } - private static boolean dumpViewWithProperties(Context context, View view, - BufferedWriter out, int level) { + private static boolean dumpView(Context context, View view, + BufferedWriter out, int level, boolean includeProperties) { try { for (int i = 0; i < level; i++) { @@ -835,7 +861,9 @@ public class ViewDebug { out.write('@'); out.write(Integer.toHexString(view.hashCode())); out.write(' '); - dumpViewProperties(context, view, out); + if (includeProperties) { + dumpViewProperties(context, view, out); + } out.newLine(); } catch (IOException e) { Log.w("View", "Error while dumping hierarchy tree"); @@ -1347,4 +1375,68 @@ public class ViewDebug { sb.append(capturedViewExportMethods(view, klass, "")); Log.d(tag, sb.toString()); } + + /** + * Invoke a particular method on given view. + * The given method is always invoked on the UI thread. The caller thread will stall until the + * method invocation is complete. Returns an object equal to the result of the method + * invocation, null if the method is declared to return void + * @throws Exception if the method invocation caused any exception + * @hide + */ + public static Object invokeViewMethod(final View view, final Method method, + final Object[] args) { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference<Object> result = new AtomicReference<Object>(); + final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); + + view.post(new Runnable() { + @Override + public void run() { + try { + result.set(method.invoke(view, args)); + } catch (InvocationTargetException e) { + exception.set(e.getCause()); + } catch (Exception e) { + exception.set(e); + } + + latch.countDown(); + } + }); + + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + if (exception.get() != null) { + throw new RuntimeException(exception.get()); + } + + return result.get(); + } + + /** + * @hide + */ + public static void setLayoutParameter(final View view, final String param, final int value) + throws NoSuchFieldException, IllegalAccessException { + final ViewGroup.LayoutParams p = view.getLayoutParams(); + final Field f = p.getClass().getField(param); + if (f.getType() != int.class) { + throw new RuntimeException("Only integer layout parameters can be set. Field " + + param + " is of type " + f.getType().getSimpleName()); + } + + f.set(p, Integer.valueOf(value)); + + view.post(new Runnable() { + @Override + public void run() { + view.setLayoutParams(p); + } + }); + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index dbbcde6..5105fda 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -35,6 +35,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; +import android.util.Pools.SynchronizedPool; import android.util.SparseArray; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -83,6 +84,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private static final String TAG = "ViewGroup"; private static final boolean DBG = false; + /** @hide */ + public static boolean DEBUG_DRAW = false; /** * Views which have been hidden or removed which need to be animated on @@ -180,10 +183,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager }) protected int mGroupFlags; - /* - * The layout mode: either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS} + /** + * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. */ - private int mLayoutMode = CLIP_BOUNDS; + private int mLayoutMode = DEFAULT_LAYOUT_MODE; /** * NOTE: If you change the flags below make sure to reflect the changes @@ -356,20 +359,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * This constant is a {@link #setLayoutMode(int) layoutMode}. * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top}, * {@link #getRight() right} and {@link #getBottom() bottom}. - * - * @hide */ - public static final int CLIP_BOUNDS = 0; + public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; /** * This constant is a {@link #setLayoutMode(int) layoutMode}. * Optical bounds describe where a widget appears to be. They sit inside the clip * bounds which need to cover a larger area to allow other effects, * such as shadows and glows, to be drawn. - * - * @hide */ - public static final int OPTICAL_BOUNDS = 1; + public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; + + /** @hide */ + public static int DEFAULT_LAYOUT_MODE = LAYOUT_MODE_CLIP_BOUNDS; /** * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL @@ -434,7 +436,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } private boolean debugDraw() { - return mAttachInfo != null && mAttachInfo.mDebugLayout; + return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout; } private void initViewGroup() { @@ -504,6 +506,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager setLayoutTransition(new LayoutTransition()); } break; + case R.styleable.ViewGroup_layoutMode: + setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE)); + break; } } @@ -2420,7 +2425,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; child.dispatchAttachedToWindow(info, - visibility | (child.mViewFlags&VISIBILITY_MASK)); + visibility | (child.mViewFlags & VISIBILITY_MASK)); } } @@ -2682,20 +2687,89 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return b; } - private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, int color) { - Paint paint = getDebugPaint(); - paint.setColor(color); + /** Return true if this ViewGroup is laying out using optical bounds. */ + boolean isLayoutModeOptical() { + return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS; + } + + Insets computeOpticalInsets() { + if (isLayoutModeOptical()) { + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; + for (int i = 0; i < mChildrenCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + Insets insets = child.getOpticalInsets(); + left = Math.max(left, insets.left); + top = Math.max(top, insets.top); + right = Math.max(right, insets.right); + bottom = Math.max(bottom, insets.bottom); + } + } + return Insets.of(left, top, right, bottom); + } else { + return Insets.NONE; + } + } + + private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { + if (x1 != x2 && y1 != y2) { + if (x1 > x2) { + int tmp = x1; x1 = x2; x2 = tmp; + } + if (y1 > y2) { + int tmp = y1; y1 = y2; y2 = tmp; + } + canvas.drawRect(x1, y1, x2, y2, paint); + } + } + + private static int sign(int x) { + return (x >= 0) ? 1 : -1; + } + + private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) { + fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); + fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); + } + + private int dipsToPixels(int dips) { + float scale = getContext().getResources().getDisplayMetrics().density; + return (int) (dips * scale + 0.5f); + } + + private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, + int lineLength, int lineWidth) { + drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); + drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); + drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); + drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); + } + + private static void fillDifference(Canvas canvas, + int x2, int y2, int x3, int y3, + int dx1, int dy1, int dx2, int dy2, Paint paint) { + int x1 = x2 - dx1; + int y1 = y2 - dy1; + + int x4 = x3 + dx2; + int y4 = y3 + dy2; - canvas.drawLines(getDebugLines(x1, y1, x2, y2), paint); + fillRect(canvas, paint, x1, y1, x4, y2); + fillRect(canvas, paint, x1, y2, x2, y3); + fillRect(canvas, paint, x3, y2, x4, y3); + fillRect(canvas, paint, x1, y3, x4, y4); } /** * @hide */ - protected void onDebugDrawMargins(Canvas canvas) { + protected void onDebugDrawMargins(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); - c.getLayoutParams().onDebugDraw(c, canvas); + c.getLayoutParams().onDebugDraw(c, canvas, paint); } } @@ -2703,26 +2777,45 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ protected void onDebugDraw(Canvas canvas) { + Paint paint = getDebugPaint(); + // Draw optical bounds - if (getLayoutMode() == OPTICAL_BOUNDS) { + { + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); Insets insets = c.getOpticalInsets(); - drawRect(canvas, - c.getLeft() + insets.left, - c.getTop() + insets.top, - c.getRight() - insets.right, - c.getBottom() - insets.bottom, Color.RED); + + drawRect(canvas, paint, + c.getLeft() + insets.left, + c.getTop() + insets.top, + c.getRight() - insets.right - 1, + c.getBottom() - insets.bottom - 1); } } // Draw margins - onDebugDrawMargins(canvas); + { + paint.setColor(Color.argb(63, 255, 0, 255)); + paint.setStyle(Paint.Style.FILL); - // Draw bounds - for (int i = 0; i < getChildCount(); i++) { - View c = getChildAt(i); - drawRect(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), Color.BLUE); + onDebugDrawMargins(canvas, paint); + } + + // Draw clip bounds + { + paint.setColor(Color.rgb(63, 127, 255)); + paint.setStyle(Paint.Style.FILL); + + int lineLength = dipsToPixels(8); + int lineWidth = dipsToPixels(1); + for (int i = 0; i < getChildCount(); i++) { + View c = getChildAt(i); + drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), + paint, lineLength, lineWidth); + } } } @@ -3604,7 +3697,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } - view.clearAccessibilityFocus(); + if (view.isAccessibilityFocused()) { + view.clearAccessibilityFocus(); + } cancelTouchTarget(view); cancelHoverTarget(view); @@ -3620,20 +3715,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childHasTransientStateChanged(view, false); } - onViewRemoved(view); - needGlobalAttributesUpdate(false); removeFromArray(index); if (clearChildFocus) { clearChildFocus(view); - ensureInputFocusOnFirstFocusable(); + if (!rootViewRequestFocus()) { + notifyGlobalFocusCleared(this); + } } - if (view.isAccessibilityFocused()) { - view.clearAccessibilityFocus(); - } + onViewRemoved(view); } /** @@ -3672,7 +3765,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private void removeViewsInternal(int start, int count) { final View focused = mFocused; final boolean detach = mAttachInfo != null; - View clearChildFocus = null; + boolean clearChildFocus = false; final View[] children = mChildren; final int end = start + count; @@ -3686,10 +3779,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (view == focused) { view.unFocus(); - clearChildFocus = view; + clearChildFocus = true; } - view.clearAccessibilityFocus(); + if (view.isAccessibilityFocused()) { + view.clearAccessibilityFocus(); + } cancelTouchTarget(view); cancelHoverTarget(view); @@ -3712,9 +3807,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager removeFromArray(start, count); - if (clearChildFocus != null) { - clearChildFocus(clearChildFocus); - ensureInputFocusOnFirstFocusable(); + if (clearChildFocus) { + clearChildFocus(focused); + if (!rootViewRequestFocus()) { + notifyGlobalFocusCleared(focused); + } } } @@ -3756,7 +3853,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View focused = mFocused; final boolean detach = mAttachInfo != null; - View clearChildFocus = null; + boolean clearChildFocus = false; needGlobalAttributesUpdate(false); @@ -3769,10 +3866,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (view == focused) { view.unFocus(); - clearChildFocus = view; + clearChildFocus = true; } - view.clearAccessibilityFocus(); + if (view.isAccessibilityFocused()) { + view.clearAccessibilityFocus(); + } cancelTouchTarget(view); cancelHoverTarget(view); @@ -3794,9 +3893,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children[i] = null; } - if (clearChildFocus != null) { - clearChildFocus(clearChildFocus); - ensureInputFocusOnFirstFocusable(); + if (clearChildFocus) { + clearChildFocus(focused); + if (!rootViewRequestFocus()) { + notifyGlobalFocusCleared(focused); + } } } @@ -4312,7 +4413,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v.mTop += offset; v.mBottom += offset; if (v.mDisplayList != null) { - v.mDisplayList.offsetTopBottom(offset); + v.mDisplayList.offsetTopAndBottom(offset); invalidateViewProperty(false, false); } } @@ -4610,13 +4711,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Returns the basis of alignment during layout operations on this view group: - * either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}. + * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. * * @return the layout mode to use during layout operations * * @see #setLayoutMode(int) - * - * @hide */ public int getLayoutMode() { return mLayoutMode; @@ -4624,15 +4723,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Sets the basis of alignment during the layout of this view group. - * Valid values are either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}. + * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or + * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. * <p> - * The default is {@link #CLIP_BOUNDS}. + * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}. * * @param layoutMode the layout mode to use during layout operations * * @see #getLayoutMode() - * - * @hide */ public void setLayoutMode(int layoutMode) { if (mLayoutMode != layoutMode) { @@ -5650,7 +5748,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @hide */ - public void onDebugDraw(View view, Canvas canvas) { + public void onDebugDraw(View view, Canvas canvas, Paint paint) { } /** @@ -5998,12 +6096,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ @Override - public void onDebugDraw(View view, Canvas canvas) { - drawRect(canvas, - view.getLeft() - leftMargin, - view.getTop() - topMargin, - view.getRight() + rightMargin, - view.getBottom() + bottomMargin, Color.MAGENTA); + public void onDebugDraw(View view, Canvas canvas, Paint paint) { + Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; + + fillDifference(canvas, + view.getLeft() + oi.left, + view.getTop() + oi.top, + view.getRight() - oi.right, + view.getBottom() - oi.bottom, + leftMargin, + topMargin, + rightMargin, + bottomMargin, + paint); } } @@ -6119,50 +6224,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private static final int MAX_POOL_SIZE = 32; - private static final Object sPoolLock = new Object(); - - private static ChildListForAccessibility sPool; - - private static int sPoolSize; - - private boolean mIsPooled; - - private ChildListForAccessibility mNext; + private static final SynchronizedPool<ChildListForAccessibility> sPool = + new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE); private final ArrayList<View> mChildren = new ArrayList<View>(); private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>(); public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) { - ChildListForAccessibility list = null; - synchronized (sPoolLock) { - if (sPool != null) { - list = sPool; - sPool = list.mNext; - list.mNext = null; - list.mIsPooled = false; - sPoolSize--; - } else { - list = new ChildListForAccessibility(); - } - list.init(parent, sort); - return list; + ChildListForAccessibility list = sPool.acquire(); + if (list == null) { + list = new ChildListForAccessibility(); } + list.init(parent, sort); + return list; } public void recycle() { - if (mIsPooled) { - throw new IllegalStateException("Instance already recycled."); - } clear(); - synchronized (sPoolLock) { - if (sPoolSize < MAX_POOL_SIZE) { - mNext = sPool; - mIsPooled = true; - sPool = this; - sPoolSize++; - } - } + sPool.release(this); } public int getChildCount() { @@ -6216,15 +6296,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private static final int MAX_POOL_SIZE = 32; - private static final Object sPoolLock = new Object(); - - private static ViewLocationHolder sPool; - - private static int sPoolSize; - - private boolean mIsPooled; - - private ViewLocationHolder mNext; + private static final SynchronizedPool<ViewLocationHolder> sPool = + new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE); private final Rect mLocation = new Rect(); @@ -6233,35 +6306,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private int mLayoutDirection; public static ViewLocationHolder obtain(ViewGroup root, View view) { - ViewLocationHolder holder = null; - synchronized (sPoolLock) { - if (sPool != null) { - holder = sPool; - sPool = holder.mNext; - holder.mNext = null; - holder.mIsPooled = false; - sPoolSize--; - } else { - holder = new ViewLocationHolder(); - } - holder.init(root, view); - return holder; + ViewLocationHolder holder = sPool.acquire(); + if (holder == null) { + holder = new ViewLocationHolder(); } + holder.init(root, view); + return holder; } public void recycle() { - if (mIsPooled) { - throw new IllegalStateException("Instance already recycled."); - } clear(); - synchronized (sPoolLock) { - if (sPoolSize < MAX_POOL_SIZE) { - mNext = sPool; - mIsPooled = true; - sPool = this; - sPoolSize++; - } - } + sPool.release(this); } @Override @@ -6337,14 +6392,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return sDebugPaint; } - private static float[] getDebugLines(int x1, int y1, int x2, int y2) { + private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { if (sDebugLines== null) { sDebugLines = new float[16]; } - x2--; - y2--; - sDebugLines[0] = x1; sDebugLines[1] = y1; sDebugLines[2] = x2; @@ -6353,18 +6405,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager sDebugLines[4] = x2; sDebugLines[5] = y1; sDebugLines[6] = x2; - sDebugLines[7] = y2 + 1; + sDebugLines[7] = y2; - sDebugLines[8] = x2 + 1; + sDebugLines[8] = x2; sDebugLines[9] = y2; sDebugLines[10] = x1; sDebugLines[11] = y2; - sDebugLines[12] = x1; - sDebugLines[13] = y2; + sDebugLines[12] = x1; + sDebugLines[13] = y2; sDebugLines[14] = x1; sDebugLines[15] = y1; - return sDebugLines; + canvas.drawLines(sDebugLines, paint); } } diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index ddff91d..4b70bc0 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -295,4 +295,105 @@ public interface ViewParent { * @hide */ public void childAccessibilityStateChanged(View child); + + /** + * Tells if this view parent can resolve the layout direction. + * See {@link View#setLayoutDirection(int)} + * + * @return True if this view parent can resolve the layout direction. + * + * @hide + */ + public boolean canResolveLayoutDirection(); + + /** + * Tells if this view parent layout direction is resolved. + * See {@link View#setLayoutDirection(int)} + * + * @return True if this view parent layout direction is resolved. + * + * @hide + */ + public boolean isLayoutDirectionResolved(); + + /** + * Return this view parent layout direction. See {@link View#getLayoutDirection()} + * + * @return {@link View#LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns + * {@link View#LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. + * + * @hide + */ + public int getLayoutDirection(); + + /** + * Tells if this view parent can resolve the text direction. + * See {@link View#setTextDirection(int)} + * + * @return True if this view parent can resolve the text direction. + * + * @hide + */ + public boolean canResolveTextDirection(); + + /** + * Tells if this view parent text direction is resolved. + * See {@link View#setTextDirection(int)} + * + * @return True if this view parent text direction is resolved. + * + * @hide + */ + public boolean isTextDirectionResolved(); + + /** + * Return this view parent text direction. See {@link View#getTextDirection()} + * + * @return the resolved text direction. Returns one of: + * + * {@link View#TEXT_DIRECTION_FIRST_STRONG} + * {@link View#TEXT_DIRECTION_ANY_RTL}, + * {@link View#TEXT_DIRECTION_LTR}, + * {@link View#TEXT_DIRECTION_RTL}, + * {@link View#TEXT_DIRECTION_LOCALE} + * + * @hide + */ + public int getTextDirection(); + + /** + * Tells if this view parent can resolve the text alignment. + * See {@link View#setTextAlignment(int)} + * + * @return True if this view parent can resolve the text alignment. + * + * @hide + */ + public boolean canResolveTextAlignment(); + + /** + * Tells if this view parent text alignment is resolved. + * See {@link View#setTextAlignment(int)} + * + * @return True if this view parent text alignment is resolved. + * + * @hide + */ + public boolean isTextAlignmentResolved(); + + /** + * Return this view parent text alignment. See {@link android.view.View#getTextAlignment()} + * + * @return the resolved text alignment. Returns one of: + * + * {@link View#TEXT_ALIGNMENT_GRAVITY}, + * {@link View#TEXT_ALIGNMENT_CENTER}, + * {@link View#TEXT_ALIGNMENT_TEXT_START}, + * {@link View#TEXT_ALIGNMENT_TEXT_END}, + * {@link View#TEXT_ALIGNMENT_VIEW_START}, + * {@link View#TEXT_ALIGNMENT_VIEW_END} + * + * @hide + */ + public int getTextAlignment(); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3b91e00..9b6dafb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -114,7 +114,7 @@ public final class ViewRootImpl implements ViewParent, * Set this system property to true to force the view hierarchy to render * at 60 Hz. This can be used to measure the potential framerate. */ - private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering"; + private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering"; private static final boolean MEASURE_LATENCY = false; private static LatencyTimer lt; @@ -139,6 +139,7 @@ public final class ViewRootImpl implements ViewParent, final IWindowSession mWindowSession; final Display mDisplay; + final String mBasePackageName; long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); @@ -169,9 +170,6 @@ public final class ViewRootImpl implements ViewParent, int mSeq; View mView; - View mFocusedView; - View mRealFocusedView; // this is not set to null in touch mode - View mOldFocusedView; View mAccessibilityFocusedHost; AccessibilityNodeInfo mAccessibilityFocusedVirtualView; @@ -276,7 +274,7 @@ public final class ViewRootImpl implements ViewParent, boolean mScrollMayChange; int mSoftInputMode; - View mLastScrolledFocus; + WeakReference<View> mLastScrolledFocus; int mScrollY; int mCurScrollY; Scroller mScroller; @@ -296,15 +294,15 @@ public final class ViewRootImpl implements ViewParent, final PointF mLastTouchPoint = new PointF(); private boolean mProfileRendering; - private Thread mRenderProfiler; - private volatile boolean mRenderProfilingEnabled; + private Choreographer.FrameCallback mRenderProfiler; + private boolean mRenderProfilingEnabled; // Variables to track frames per second, enabled via DEBUG_FPS flag private long mFpsStartTime = -1; private long mFpsPrevTime = -1; private int mFpsNumFrames; - private final ArrayList<DisplayList> mDisplayLists = new ArrayList<DisplayList>(24); + private final ArrayList<DisplayList> mDisplayLists = new ArrayList<DisplayList>(); /** * see {@link #playSoundEffect(int)} @@ -324,6 +322,10 @@ public final class ViewRootImpl implements ViewParent, private final int mDensity; private final int mNoncompatDensity; + private boolean mInLayout = false; + ArrayList<View> mLayoutRequesters = new ArrayList<View>(); + boolean mHandlingLayoutInLayoutRequest = false; + private int mViewLayoutDirectionInitial; /** @@ -354,6 +356,7 @@ public final class ViewRootImpl implements ViewParent, // allow the spawning of threads. mWindowSession = WindowManagerGlobal.getWindowSession(context.getMainLooper()); mDisplay = display; + mBasePackageName = context.getBasePackageName(); CompatibilityInfoHolder cih = display.getCompatibilityInfo(); mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder(); @@ -385,8 +388,6 @@ public final class ViewRootImpl implements ViewParent, mDensity = context.getResources().getDisplayMetrics().densityDpi; mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); - mProfileRendering = Boolean.parseBoolean( - SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false")); mChoreographer = Choreographer.getInstance(); PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); @@ -478,6 +479,9 @@ public final class ViewRootImpl implements ViewParent, mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); + if (mWindowAttributes.packageName == null) { + mWindowAttributes.packageName = mBasePackageName; + } attrs = mWindowAttributes; // Keep track of the actual window flags supplied by the client. mClientWindowLayoutFlags = attrs.flags; @@ -740,6 +744,7 @@ public final class ViewRootImpl implements ViewParent, final boolean translucent = attrs.format != PixelFormat.OPAQUE; mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); + mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = mAttachInfo.mHardwareRenderer != null; @@ -774,6 +779,9 @@ public final class ViewRootImpl implements ViewParent, attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility; attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility; mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs); + if (mWindowAttributes.packageName == null) { + mWindowAttributes.packageName = mBasePackageName; + } mWindowAttributes.flags |= compatibleWindowFlag; applyKeepScreenOnFlag(mWindowAttributes); @@ -830,9 +838,11 @@ public final class ViewRootImpl implements ViewParent, @Override public void requestLayout() { - checkThread(); - mLayoutRequested = true; - scheduleTraversals(); + if (!mHandlingLayoutInLayoutRequest) { + checkThread(); + mLayoutRequested = true; + scheduleTraversals(); + } } @Override @@ -1427,8 +1437,6 @@ public final class ViewRootImpl implements ViewParent, } // TODO: should handle create/resize failure layerCanvas = mResizeBuffer.start(hwRendererCanvas); - layerCanvas.setViewport(mWidth, mHeight); - layerCanvas.onPreDraw(null); final int restoreCount = layerCanvas.save(); int yoff; @@ -1465,9 +1473,6 @@ public final class ViewRootImpl implements ViewParent, } catch (OutOfMemoryError e) { Log.w(TAG, "Not enough memory for content change anim buffer", e); } finally { - if (layerCanvas != null) { - layerCanvas.onPostDraw(); - } if (mResizeBuffer != null) { mResizeBuffer.end(hwRendererCanvas); if (!completed) { @@ -1512,16 +1517,7 @@ public final class ViewRootImpl implements ViewParent, hwInitialized = mAttachInfo.mHardwareRenderer.initialize( mHolder.getSurface()); } catch (Surface.OutOfResourcesException e) { - Log.e(TAG, "OutOfResourcesException initializing HW surface", e); - try { - if (!mWindowSession.outOfMemory(mWindow) && - Process.myUid() != Process.SYSTEM_UID) { - Slog.w(TAG, "No processes killed for memory; killing self"); - Process.killProcess(Process.myPid()); - } - } catch (RemoteException ex) { - } - mLayoutRequested = true; // ask wm for a new surface next time. + handleOutOfResourcesException(e); return; } } @@ -1529,7 +1525,9 @@ public final class ViewRootImpl implements ViewParent, } else if (!mSurface.isValid()) { // If the surface has been removed, then reset the scroll // positions. - mLastScrolledFocus = null; + if (mLastScrolledFocus != null) { + mLastScrolledFocus.clear(); + } mScrollY = mCurScrollY = 0; if (mScroller != null) { mScroller.abortAnimation(); @@ -1546,15 +1544,7 @@ public final class ViewRootImpl implements ViewParent, try { mAttachInfo.mHardwareRenderer.updateSurface(mHolder.getSurface()); } catch (Surface.OutOfResourcesException e) { - Log.e(TAG, "OutOfResourcesException updating HW surface", e); - try { - if (!mWindowSession.outOfMemory(mWindow)) { - Slog.w(TAG, "No processes killed for memory; killing self"); - Process.killProcess(Process.myPid()); - } - } catch (RemoteException ex) { - } - mLayoutRequested = true; // ask wm for a new surface next time. + handleOutOfResourcesException(e); return; } } @@ -1718,7 +1708,7 @@ public final class ViewRootImpl implements ViewParent, boolean triggerGlobalLayoutListener = didLayout || attachInfo.mRecomputeGlobalAttributes; if (didLayout) { - performLayout(); + performLayout(lp, desiredWindowWidth, desiredWindowHeight); // By this point all views have been sized and positionned // We can compute the transparent area @@ -1805,13 +1795,11 @@ public final class ViewRootImpl implements ViewParent, if (mView != null) { if (!mView.hasFocus()) { mView.requestFocus(View.FOCUS_FORWARD); - mFocusedView = mRealFocusedView = mView.findFocus(); if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view=" - + mFocusedView); + + mView.findFocus()); } else { - mRealFocusedView = mView.findFocus(); if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view=" - + mRealFocusedView); + + mView.findFocus()); } } if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) { @@ -1878,6 +1866,19 @@ public final class ViewRootImpl implements ViewParent, mIsInTraversal = false; } + private void handleOutOfResourcesException(Surface.OutOfResourcesException e) { + Log.e(TAG, "OutOfResourcesException initializing HW surface", e); + try { + if (!mWindowSession.outOfMemory(mWindow) && + Process.myUid() != Process.SYSTEM_UID) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } + mLayoutRequested = true; // ask wm for a new surface next time. + } + private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { @@ -1887,9 +1888,66 @@ public final class ViewRootImpl implements ViewParent, } } - private void performLayout() { + /** + * Called by {@link android.view.View#isInLayout()} to determine whether the view hierarchy + * is currently undergoing a layout pass. + * + * @return whether the view hierarchy is currently undergoing a layout pass + */ + boolean isInLayout() { + return mInLayout; + } + + /** + * Called by {@link android.view.View#requestLayout()} if the view hierarchy is currently + * undergoing a layout pass. requestLayout() should not generally be called during layout, + * unless the container hierarchy knows what it is doing (i.e., it is fine as long as + * all children in that container hierarchy are measured and laid out at the end of the layout + * pass for that container). If requestLayout() is called anyway, we handle it correctly + * by registering all requesters during a frame as it proceeds. At the end of the frame, + * we check all of those views to see if any still have pending layout requests, which + * indicates that they were not correctly handled by their container hierarchy. If that is + * the case, we clear all such flags in the tree, to remove the buggy flag state that leads + * to blank containers, and force a second request/measure/layout pass in this frame. If + * more requestLayout() calls are received during that second layout pass, we post those + * requests to the next frame to avoid possible infinite loops. + * + * <p>The return value from this method indicates whether the request should proceed + * (if it is a request during the first layout pass) or should be skipped and posted to the + * next frame (if it is a request during the second layout pass).</p> + * + * @param view the view that requested the layout. + * + * @return true if request should proceed, false otherwise. + */ + boolean requestLayoutDuringLayout(final View view) { + if (view.mParent == null || view.mAttachInfo == null) { + // Would not normally trigger another layout, so just let it pass through as usual + return true; + } + if (!mHandlingLayoutInLayoutRequest) { + if (!mLayoutRequesters.contains(view)) { + mLayoutRequesters.add(view); + } + return true; + } else { + Log.w("View", "requestLayout() called by " + view + " during second layout pass: " + + "posting to next frame"); + view.post(new Runnable() { + @Override + public void run() { + view.requestLayout(); + } + }); + return false; + } + } + + private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, + int desiredWindowHeight) { mLayoutRequested = false; mScrollMayChange = true; + mInLayout = true; final View host = mView; if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { @@ -1900,9 +1958,72 @@ public final class ViewRootImpl implements ViewParent, Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); + + mInLayout = false; + int numViewsRequestingLayout = mLayoutRequesters.size(); + if (numViewsRequestingLayout > 0) { + // requestLayout() was called during layout. + // If no layout-request flags are set on the requesting views, there is no problem. + // If some requests are still pending, then we need to clear those flags and do + // a full request/measure/layout pass to handle this situation. + + // Check state of layout flags for all requesters + ArrayList<View> mValidLayoutRequesters = null; + for (int i = 0; i < numViewsRequestingLayout; ++i) { + View view = mLayoutRequesters.get(i); + if ((view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) == View.PFLAG_FORCE_LAYOUT) { + while (view != null && view.mAttachInfo != null && view.mParent != null && + (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) { + if ((view.mViewFlags & View.VISIBILITY_MASK) != View.GONE) { + // Only trigger new requests for non-GONE views + Log.w(TAG, "requestLayout() improperly called during " + + "layout: running second layout pass for " + view); + if (mValidLayoutRequesters == null) { + mValidLayoutRequesters = new ArrayList<View>(); + } + mValidLayoutRequesters.add(view); + break; + } + if (view.mParent instanceof View) { + view = (View) view.mParent; + } else { + view = null; + } + } + } + } + if (mValidLayoutRequesters != null) { + // Clear flags throughout hierarchy, walking up from each flagged requester + for (int i = 0; i < numViewsRequestingLayout; ++i) { + View view = mLayoutRequesters.get(i); + while (view != null && + (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) { + view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT; + if (view.mParent instanceof View) { + view = (View) view.mParent; + } else { + view = null; + } + } + } + // Process fresh layout requests, then measure and layout + mHandlingLayoutInLayoutRequest = true; + int numValidRequests = mValidLayoutRequesters.size(); + for (int i = 0; i < numValidRequests; ++i) { + mValidLayoutRequesters.get(i).requestLayout(); + } + measureHierarchy(host, lp, mView.getContext().getResources(), + desiredWindowWidth, desiredWindowHeight); + mInLayout = true; + host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); + mHandlingLayoutInLayoutRequest = false; + } + mLayoutRequesters.clear(); + } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + mInLayout = false; } public void requestTransparentRegion(View child) { @@ -1985,31 +2106,25 @@ public final class ViewRootImpl implements ViewParent, private void profileRendering(boolean enabled) { if (mProfileRendering) { mRenderProfilingEnabled = enabled; - if (mRenderProfiler == null) { - mRenderProfiler = new Thread(new Runnable() { - @Override - public void run() { - Log.d(TAG, "Starting profiling thread"); - while (mRenderProfilingEnabled) { - mAttachInfo.mHandler.post(new Runnable() { - @Override - public void run() { - mDirty.set(0, 0, mWidth, mHeight); - scheduleTraversals(); - } - }); - try { - // TODO: This should use vsync when we get an API - Thread.sleep(15); - } catch (InterruptedException e) { - Log.d(TAG, "Exiting profiling thread"); - } + + if (mRenderProfiler != null) { + mChoreographer.removeFrameCallback(mRenderProfiler); + } + if (mRenderProfilingEnabled) { + if (mRenderProfiler == null) { + mRenderProfiler = new Choreographer.FrameCallback() { + @Override + public void doFrame(long frameTimeNanos) { + mDirty.set(0, 0, mWidth, mHeight); + scheduleTraversals(); + if (mRenderProfilingEnabled) { + mChoreographer.postFrameCallback(mRenderProfiler); + } } - } - }, "Rendering Profiler"); - mRenderProfiler.start(); + }; + } + mChoreographer.postFrameCallback(mRenderProfiler); } else { - mRenderProfiler.interrupt(); mRenderProfiler = null; } } @@ -2085,7 +2200,7 @@ public final class ViewRootImpl implements ViewParent, private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; - if (surface == null || !surface.isValid()) { + if (!surface.isValid()) { return; } @@ -2166,6 +2281,8 @@ public final class ViewRootImpl implements ViewParent, appScale + ", width=" + mWidth + ", height=" + mHeight); } + invalidateDisplayLists(); + attachInfo.mTreeObserver.dispatchOnDraw(); if (!dirty.isEmpty() || mIsAnimating) { @@ -2184,8 +2301,35 @@ public final class ViewRootImpl implements ViewParent, animating ? null : mCurrentDirty)) { mPreviousDirty.set(0, 0, mWidth, mHeight); } - } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { - return; + } else { + // If we get here with a disabled & requested hardware renderer, something went + // wrong (an invalidate posted right before we destroyed the hardware surface + // for instance) so we should just bail out. Locking the surface with software + // rendering at this point would lock it forever and prevent hardware renderer + // from doing its job when it comes back. + // Before we request a new frame we must however attempt to reinitiliaze the + // hardware renderer if it's in requested state. This would happen after an + // eglTerminate() for instance. + if (attachInfo.mHardwareRenderer != null && + !attachInfo.mHardwareRenderer.isEnabled() && + attachInfo.mHardwareRenderer.isRequested()) { + + try { + attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, + mHolder.getSurface()); + } catch (Surface.OutOfResourcesException e) { + handleOutOfResourcesException(e); + return; + } + + mFullRedrawNeeded = true; + scheduleTraversals(); + return; + } + + if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { + return; + } } } @@ -2201,18 +2345,6 @@ public final class ViewRootImpl implements ViewParent, private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty) { - // If we get here with a disabled & requested hardware renderer, something went - // wrong (an invalidate posted right before we destroyed the hardware surface - // for instance) so we should just bail out. Locking the surface with software - // rendering at this point would lock it forever and prevent hardware renderer - // from doing its job when it comes back. - if (attachInfo.mHardwareRenderer != null && !attachInfo.mHardwareRenderer.isEnabled() && - attachInfo.mHardwareRenderer.isRequested()) { - mFullRedrawNeeded = true; - scheduleTraversals(); - return false; - } - // Draw with software renderer. Canvas canvas; try { @@ -2231,15 +2363,7 @@ public final class ViewRootImpl implements ViewParent, // TODO: Do this in native canvas.setDensity(mDensity); } catch (Surface.OutOfResourcesException e) { - Log.e(TAG, "OutOfResourcesException locking surface", e); - try { - if (!mWindowSession.outOfMemory(mWindow)) { - Slog.w(TAG, "No processes killed for memory; killing self"); - Process.killProcess(Process.myPid()); - } - } catch (RemoteException ex) { - } - mLayoutRequested = true; // ask wm for a new surface next time. + handleOutOfResourcesException(e); return false; } catch (IllegalArgumentException e) { Log.e(TAG, "Could not lock surface", e); @@ -2376,8 +2500,9 @@ public final class ViewRootImpl implements ViewParent, for (int i = 0; i < count; i++) { final DisplayList displayList = displayLists.get(i); - displayList.invalidate(); - displayList.clear(); + if (displayList.isDirty()) { + displayList.clear(); + } } displayLists.clear(); @@ -2403,17 +2528,12 @@ public final class ViewRootImpl implements ViewParent, // requestChildRectangleOnScreen() call (in which case 'rectangle' // is non-null and we just want to scroll to whatever that // rectangle is). - View focus = mRealFocusedView; - - // When in touch mode, focus points to the previously focused view, - // which may have been removed from the view hierarchy. The following - // line checks whether the view is still in our hierarchy. - if (focus == null || focus.mAttachInfo != mAttachInfo) { - mRealFocusedView = null; + View focus = mView.findFocus(); + if (focus == null) { return false; } - - if (focus != mLastScrolledFocus) { + View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null; + if (lastScrolledFocus != null && focus != lastScrolledFocus) { // If the focus has changed, then ignore any requests to scroll // to a rectangle; first we want to make sure the entire focus // view is visible. @@ -2422,8 +2542,7 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus + " rectangle=" + rectangle + " ci=" + ci + " vi=" + vi); - if (focus == mLastScrolledFocus && !mScrollMayChange - && rectangle == null) { + if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) { // Optimization: if the focus hasn't changed since last // time, and no layout has happened, then just leave things // as they are. @@ -2433,7 +2552,7 @@ public final class ViewRootImpl implements ViewParent, // We need to determine if the currently focused view is // within the visible part of the window and, if not, apply // a pan so it can be seen. - mLastScrolledFocus = focus; + mLastScrolledFocus = new WeakReference<View>(focus); mScrollMayChange = false; if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?"); // Try to find the rectangle from the focus view. @@ -2560,33 +2679,19 @@ public final class ViewRootImpl implements ViewParent, } public void requestChildFocus(View child, View focused) { - checkThread(); - if (DEBUG_INPUT_RESIZE) { Log.v(TAG, "Request child focus: focus now " + focused); } - - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused); + checkThread(); scheduleTraversals(); - - mFocusedView = mRealFocusedView = focused; } public void clearChildFocus(View child) { - checkThread(); - if (DEBUG_INPUT_RESIZE) { Log.v(TAG, "Clearing child focus"); } - - mOldFocusedView = mFocusedView; - - // Invoke the listener only if there is no view to take focus - if (focusSearch(null, View.FOCUS_FORWARD) == null) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null); - } - - mFocusedView = mRealFocusedView = null; + checkThread(); + scheduleTraversals(); } @Override @@ -2603,14 +2708,13 @@ public final class ViewRootImpl implements ViewParent, // the one case where will transfer focus away from the current one // is if the current view is a view group that prefers to give focus // to its children first AND the view is a descendant of it. - mFocusedView = mView.findFocus(); - boolean descendantsHaveDibsOnFocus = - (mFocusedView instanceof ViewGroup) && - (((ViewGroup) mFocusedView).getDescendantFocusability() == - ViewGroup.FOCUS_AFTER_DESCENDANTS); - if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() - v.requestFocus(); + View focused = mView.findFocus(); + if (focused instanceof ViewGroup) { + ViewGroup group = (ViewGroup) focused; + if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS + && isViewDescendantOf(v, focused)) { + v.requestFocus(); + } } } } @@ -2751,11 +2855,10 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_UPDATE_CONFIGURATION = 18; private final static int MSG_PROCESS_INPUT_EVENTS = 19; private final static int MSG_DISPATCH_SCREEN_STATE = 20; - private final static int MSG_INVALIDATE_DISPLAY_LIST = 21; - private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 22; - private final static int MSG_DISPATCH_DONE_ANIMATING = 23; - private final static int MSG_INVALIDATE_WORLD = 24; - private final static int MSG_WINDOW_MOVED = 25; + private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; + private final static int MSG_DISPATCH_DONE_ANIMATING = 22; + private final static int MSG_INVALIDATE_WORLD = 23; + private final static int MSG_WINDOW_MOVED = 24; final class ViewRootHandler extends Handler { @Override @@ -2801,8 +2904,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_PROCESS_INPUT_EVENTS"; case MSG_DISPATCH_SCREEN_STATE: return "MSG_DISPATCH_SCREEN_STATE"; - case MSG_INVALIDATE_DISPLAY_LIST: - return "MSG_INVALIDATE_DISPLAY_LIST"; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; case MSG_DISPATCH_DONE_ANIMATING: @@ -2822,7 +2923,7 @@ public final class ViewRootImpl implements ViewParent, case MSG_INVALIDATE_RECT: final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; info.target.invalidate(info.left, info.top, info.right, info.bottom); - info.release(); + info.recycle(); break; case MSG_IME_FINISHED_EVENT: handleImeFinishedEvent(msg.arg1, msg.arg2 != 0); @@ -2905,10 +3006,8 @@ public final class ViewRootImpl implements ViewParent, mSurface != null && mSurface.isValid()) { mFullRedrawNeeded = true; try { - if (mAttachInfo.mHardwareRenderer.initializeIfNeeded( - mWidth, mHeight, mHolder.getSurface())) { - mFullRedrawNeeded = true; - } + mAttachInfo.mHardwareRenderer.initializeIfNeeded( + mWidth, mHeight, mHolder.getSurface()); } catch (Surface.OutOfResourcesException e) { Log.e(TAG, "OutOfResourcesException locking surface", e); try { @@ -3009,7 +3108,7 @@ public final class ViewRootImpl implements ViewParent, handleDragEvent(event); } break; case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: { - handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj); + handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj); } break; case MSG_UPDATE_CONFIGURATION: { Configuration config = (Configuration)msg.obj; @@ -3023,9 +3122,6 @@ public final class ViewRootImpl implements ViewParent, handleScreenStateChange(msg.arg1 == 1); } } break; - case MSG_INVALIDATE_DISPLAY_LIST: { - invalidateDisplayLists(); - } break; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { setAccessibilityFocus(null, null); } break; @@ -3094,7 +3190,6 @@ public final class ViewRootImpl implements ViewParent, // set yet. final View focused = mView.findFocus(); if (focused != null && !focused.isFocusableInTouchMode()) { - final ViewGroup ancestorToTakeFocus = findAncestorToTakeFocusInTouchMode(focused); if (ancestorToTakeFocus != null) { @@ -3103,10 +3198,7 @@ public final class ViewRootImpl implements ViewParent, return ancestorToTakeFocus.requestFocus(); } else { // nothing appropriate to have focus in touch mode, clear it out - mView.unFocus(); - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); - mFocusedView = null; - mOldFocusedView = null; + focused.unFocus(); return true; } } @@ -3141,12 +3233,11 @@ public final class ViewRootImpl implements ViewParent, private boolean leaveTouchMode() { if (mView != null) { if (mView.hasFocus()) { - // i learned the hard way to not trust mFocusedView :) - mFocusedView = mView.findFocus(); - if (!(mFocusedView instanceof ViewGroup)) { + View focusedView = mView.findFocus(); + if (!(focusedView instanceof ViewGroup)) { // some view has focus, let it keep it return false; - } else if (((ViewGroup)mFocusedView).getDescendantFocusability() != + } else if (((ViewGroup) focusedView).getDescendantFocusability() != ViewGroup.FOCUS_AFTER_DESCENDANTS) { // some view group has focus, and doesn't prefer its children // over itself for focus, so let them keep it. @@ -4066,6 +4157,7 @@ public final class ViewRootImpl implements ViewParent, } if (mAdded && !mFirst) { + invalidateDisplayLists(); destroyHardwareRenderer(); if (mView != null) { @@ -4098,14 +4190,30 @@ public final class ViewRootImpl implements ViewParent, } public void loadSystemProperties() { - boolean layout = SystemProperties.getBoolean( - View.DEBUG_LAYOUT_PROPERTY, false); - if (layout != mAttachInfo.mDebugLayout) { - mAttachInfo.mDebugLayout = layout; - if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { - mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200); + mHandler.post(new Runnable() { + @Override + public void run() { + // Profiling + mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false); + profileRendering(mAttachInfo.mHasWindowFocus); + + // Hardware rendering + if (mAttachInfo.mHardwareRenderer != null) { + if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mHolder.getSurface())) { + invalidate(); + } + } + + // Layout debugging + boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false); + if (layout != mAttachInfo.mDebugLayout) { + mAttachInfo.mDebugLayout = layout; + if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { + mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200); + } + } } - } + }); } private void destroyHardwareRenderer() { @@ -4437,7 +4545,7 @@ public final class ViewRootImpl implements ViewParent, AttachInfo.InvalidateInfo info = mViewRects.get(i); if (info.target == view) { mViewRects.remove(i); - info.release(); + info.recycle(); } } @@ -4478,7 +4586,7 @@ public final class ViewRootImpl implements ViewParent, for (int i = 0; i < viewRectCount; i++) { final View.AttachInfo.InvalidateInfo info = mTempViewRects[i]; info.target.invalidate(info.left, info.top, info.right, info.bottom); - info.release(); + info.recycle(); } } @@ -4513,19 +4621,6 @@ public final class ViewRootImpl implements ViewParent, public void enqueueDisplayList(DisplayList displayList) { mDisplayLists.add(displayList); - - mHandler.removeMessages(MSG_INVALIDATE_DISPLAY_LIST); - Message msg = mHandler.obtainMessage(MSG_INVALIDATE_DISPLAY_LIST); - mHandler.sendMessage(msg); - } - - public void dequeueDisplayList(DisplayList displayList) { - if (mDisplayLists.remove(displayList)) { - displayList.invalidate(); - if (mDisplayLists.size() == 0) { - mHandler.removeMessages(MSG_INVALIDATE_DISPLAY_LIST); - } - } } public void cancelInvalidate(View view) { @@ -4727,6 +4822,51 @@ public final class ViewRootImpl implements ViewParent, postSendWindowContentChangedCallback(child); } + @Override + public boolean canResolveLayoutDirection() { + return true; + } + + @Override + public boolean isLayoutDirectionResolved() { + return true; + } + + @Override + public int getLayoutDirection() { + return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT; + } + + @Override + public boolean canResolveTextDirection() { + return true; + } + + @Override + public boolean isTextDirectionResolved() { + return true; + } + + @Override + public int getTextDirection() { + return View.TEXT_DIRECTION_RESOLVED_DEFAULT; + } + + @Override + public boolean canResolveTextAlignment() { + return true; + } + + @Override + public boolean isTextAlignmentResolved() { + return true; + } + + @Override + public int getTextAlignment() { + return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT; + } + private View getCommonPredecessor(View first, View second) { if (mAttachInfo != null) { if (mTempHashSet == null) { @@ -5362,12 +5502,13 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5399,14 +5540,16 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, - int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, + String viewId, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() - .findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId, + viewId, interactionId, callback, flags, interrogatingPid, + interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5420,12 +5563,13 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5439,12 +5583,12 @@ public final class ViewRootImpl implements ViewParent, @Override public void findFocus(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback, - flags, interrogatingPid, interrogatingTid); + flags, interrogatingPid, interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5458,12 +5602,12 @@ public final class ViewRootImpl implements ViewParent, @Override public void focusSearch(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .focusSearchClientThread(accessibilityNodeId, direction, interactionId, - callback, flags, interrogatingPid, interrogatingTid); + callback, flags, interrogatingPid, interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index 001d020..e711b94 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -218,12 +218,14 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private static class WarningDialogReceiver extends BroadcastReceiver implements DialogInterface.OnDismissListener { - private Context mContext; - private Dialog mDialog; + private final Context mContext; + private final Dialog mDialog; + private final VolumePanel mVolumePanel; - WarningDialogReceiver(Context context, Dialog dialog) { + WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) { mContext = context; mDialog = dialog; + mVolumePanel = volumePanel; IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.registerReceiver(this, filter); } @@ -231,16 +233,20 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie @Override public void onReceive(Context context, Intent intent) { mDialog.cancel(); - synchronized (sConfirmSafeVolumeLock) { - sConfirmSafeVolumeDialog = null; - } + cleanUp(); } public void onDismiss(DialogInterface unused) { mContext.unregisterReceiver(this); + cleanUp(); + } + + private void cleanUp() { synchronized (sConfirmSafeVolumeLock) { sConfirmSafeVolumeDialog = null; } + mVolumePanel.forceTimeout(); + mVolumePanel.updateStates(); } } @@ -276,7 +282,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mDialog = new Dialog(context, R.style.Theme_Panel_Volume) { public boolean onTouchEvent(MotionEvent event) { - if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE) { + if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE && + sConfirmSafeVolumeDialog == null) { forceTimeout(); return true; } @@ -330,6 +337,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie listenToRingerMode(); } + public void setLayoutDirection(int layoutDirection) { + mPanel.setLayoutDirection(layoutDirection); + updateStates(); + } + private void listenToRingerMode() { final IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); @@ -453,6 +465,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private void updateSlider(StreamControl sc) { sc.seekbarView.setProgress(getStreamVolume(sc.streamType)); final boolean muted = isMuted(sc.streamType); + // Force reloading the image resource + sc.icon.setImageDrawable(null); sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes); if (sc.streamType == AudioManager.STREAM_RING && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { @@ -462,7 +476,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie // never disable touch interactions for remote playback, the muting is not tied to // the state of the phone. sc.seekbarView.setEnabled(true); - } else if (sc.streamType != mAudioManager.getMasterStreamType() && muted) { + } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) || + (sConfirmSafeVolumeDialog != null)) { sc.seekbarView.setEnabled(false); } else { sc.seekbarView.setEnabled(true); @@ -491,7 +506,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } } - private void updateStates() { + public void updateStates() { final int count = mSliderGroup.getChildCount(); for (int i = 0; i < count; i++) { StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag(); @@ -563,9 +578,9 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie postMuteChanged(STREAM_MASTER, flags); } - public void postDisplaySafeVolumeWarning() { + public void postDisplaySafeVolumeWarning(int flags) { if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return; - obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, 0, 0).sendToTarget(); + obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget(); } /** @@ -599,7 +614,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie removeMessages(MSG_FREE_RESOURCES); sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); - resetTimeout(); } @@ -705,7 +719,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) || (streamType != mAudioManager.getMasterStreamType() && streamType != AudioService.STREAM_REMOTE_MUSIC && - isMuted(streamType))) { + isMuted(streamType)) || + sConfirmSafeVolumeDialog != null) { sc.seekbarView.setEnabled(false); } else { sc.seekbarView.setEnabled(true); @@ -803,7 +818,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie removeMessages(MSG_FREE_RESOURCES); sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); - resetTimeout(); } @@ -839,30 +853,34 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } } - protected void onDisplaySafeVolumeWarning() { - synchronized (sConfirmSafeVolumeLock) { - if (sConfirmSafeVolumeDialog != null) { - return; + protected void onDisplaySafeVolumeWarning(int flags) { + if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) { + synchronized (sConfirmSafeVolumeLock) { + if (sConfirmSafeVolumeDialog != null) { + return; + } + sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext) + .setMessage(com.android.internal.R.string.safe_media_volume_warning) + .setPositiveButton(com.android.internal.R.string.yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mAudioService.disableSafeMediaVolume(); + } + }) + .setNegativeButton(com.android.internal.R.string.no, null) + .setIconAttribute(android.R.attr.alertDialogIcon) + .create(); + final WarningDialogReceiver warning = new WarningDialogReceiver(mContext, + sConfirmSafeVolumeDialog, this); + + sConfirmSafeVolumeDialog.setOnDismissListener(warning); + sConfirmSafeVolumeDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + sConfirmSafeVolumeDialog.show(); } - sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext) - .setMessage(com.android.internal.R.string.safe_media_volume_warning) - .setPositiveButton(com.android.internal.R.string.yes, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mAudioService.disableSafeMediaVolume(); - } - }) - .setNegativeButton(com.android.internal.R.string.no, null) - .setIconAttribute(android.R.attr.alertDialogIcon) - .create(); - final WarningDialogReceiver warning = new WarningDialogReceiver(mContext, - sConfirmSafeVolumeDialog); - - sConfirmSafeVolumeDialog.setOnDismissListener(warning); - sConfirmSafeVolumeDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - sConfirmSafeVolumeDialog.show(); + updateStates(); } + resetTimeout(); } /** @@ -958,6 +976,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mDialog.dismiss(); mActiveStreamType = -1; } + synchronized (sConfirmSafeVolumeLock) { + if (sConfirmSafeVolumeDialog != null) { + sConfirmSafeVolumeDialog.dismiss(); + } + } break; } case MSG_RINGER_MODE_CHANGED: { @@ -981,7 +1004,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie break; case MSG_DISPLAY_SAFE_VOLUME_WARNING: - onDisplaySafeVolumeWarning(); + onDisplaySafeVolumeWarning(msg.arg1); break; } } diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java deleted file mode 100644 index 7d16e14..0000000 --- a/core/java/android/view/WindowInfo.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view; - -import android.graphics.Rect; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Information the state of a window. - * - * @hide - */ -public class WindowInfo implements Parcelable { - - private static final int MAX_POOL_SIZE = 20; - - private static int UNDEFINED = -1; - - private static Object sPoolLock = new Object(); - private static WindowInfo sPool; - private static int sPoolSize; - - private WindowInfo mNext; - private boolean mInPool; - - public IBinder token; - - public final Rect frame = new Rect(); - - public final Rect touchableRegion = new Rect(); - - public int type = UNDEFINED; - - public float compatibilityScale = UNDEFINED; - - public boolean visible; - - public int displayId = UNDEFINED; - - public int layer = UNDEFINED; - - private WindowInfo() { - /* do nothing - reduce visibility */ - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeStrongBinder(token); - parcel.writeParcelable(frame, 0); - parcel.writeParcelable(touchableRegion, 0); - parcel.writeInt(type); - parcel.writeFloat(compatibilityScale); - parcel.writeInt(visible ? 1 : 0); - parcel.writeInt(displayId); - parcel.writeInt(layer); - recycle(); - } - - private void initFromParcel(Parcel parcel) { - token = parcel.readStrongBinder(); - frame.set((Rect) parcel.readParcelable(null)); - touchableRegion.set((Rect) parcel.readParcelable(null)); - type = parcel.readInt(); - compatibilityScale = parcel.readFloat(); - visible = (parcel.readInt() == 1); - displayId = parcel.readInt(); - layer = parcel.readInt(); - } - - public static WindowInfo obtain(WindowInfo other) { - WindowInfo info = obtain(); - info.token = other.token; - info.frame.set(other.frame); - info.touchableRegion.set(other.touchableRegion); - info.type = other.type; - info.compatibilityScale = other.compatibilityScale; - info.visible = other.visible; - info.displayId = other.displayId; - info.layer = other.layer; - return info; - } - - public static WindowInfo obtain() { - synchronized (sPoolLock) { - if (sPoolSize > 0) { - WindowInfo info = sPool; - sPool = info.mNext; - info.mNext = null; - info.mInPool = false; - sPoolSize--; - return info; - } else { - return new WindowInfo(); - } - } - } - - public void recycle() { - if (mInPool) { - throw new IllegalStateException("Already recycled."); - } - clear(); - synchronized (sPoolLock) { - if (sPoolSize < MAX_POOL_SIZE) { - mNext = sPool; - sPool = this; - mInPool = true; - sPoolSize++; - } - } - } - - private void clear() { - token = null; - frame.setEmpty(); - touchableRegion.setEmpty(); - type = UNDEFINED; - compatibilityScale = UNDEFINED; - visible = false; - displayId = UNDEFINED; - layer = UNDEFINED; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Window [token:").append((token != null) ? token.hashCode() : null); - builder.append(", displayId:").append(displayId); - builder.append(", type:").append(type); - builder.append(", visible:").append(visible); - builder.append(", layer:").append(layer); - builder.append(", compatibilityScale:").append(compatibilityScale); - builder.append(", frame:").append(frame); - builder.append(", touchableRegion:").append(touchableRegion); - builder.append("]"); - return builder.toString(); - } - - /** - * @see Parcelable.Creator - */ - public static final Parcelable.Creator<WindowInfo> CREATOR = - new Parcelable.Creator<WindowInfo>() { - public WindowInfo createFromParcel(Parcel parcel) { - WindowInfo info = WindowInfo.obtain(); - info.initFromParcel(parcel); - return info; - } - - public WindowInfo[] newArray(int size) { - return new WindowInfo[size]; - } - }; -} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 6a67d8b..d236561 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -735,20 +735,20 @@ public interface WindowManager extends ViewManager { /** * <p>Indicates whether this window should be hardware accelerated. * Requesting hardware acceleration does not guarantee it will happen.</p> - * + * * <p>This flag can be controlled programmatically <em>only</em> to enable * hardware acceleration. To enable hardware acceleration for a given * window programmatically, do the following:</p> - * + * * <pre> * Window w = activity.getWindow(); // in Activity's onCreate() for instance * w.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, * WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); * </pre> - * + * * <p>It is important to remember that this flag <strong>must</strong> * be set before setting the content view of your activity or dialog.</p> - * + * * <p>This flag cannot be used to disable hardware acceleration after it * was enabled in your manifest using * {@link android.R.attr#hardwareAccelerated}. If you need to selectively @@ -756,13 +756,19 @@ public interface WindowManager extends ViewManager { * for instance), make sure it is turned off in your manifest and enable it * on your activity or dialog when you need it instead, using the method * described above.</p> - * + * * <p>This flag is automatically set by the system if the * {@link android.R.attr#hardwareAccelerated android:hardwareAccelerated} * XML attribute is set to true on an activity or on the application.</p> */ public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000; + /** Window flag: allow window contents to extend in to the screen's + * overscan area, if there is one. The window should still correctly + * position its contents to take the overscan area into account. + */ + public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000; + // ----- HIDDEN FLAGS. // These start at the high bit and go down. diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index e8945aa..7eb26fa 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -160,6 +160,29 @@ public final class WindowManagerGlobal { } } + public String[] getViewRootNames() { + synchronized (mLock) { + if (mRoots == null) return new String[0]; + String[] mViewRoots = new String[mRoots.length]; + int i = 0; + for (ViewRootImpl root : mRoots) { + mViewRoots[i++] = getWindowName(root); + } + return mViewRoots; + } + } + + public View getRootView(String name) { + synchronized (mLock) { + if (mRoots == null) return null; + for (ViewRootImpl root : mRoots) { + if (name.equals(getWindowName(root))) return root.getView(); + } + } + + return null; + } + public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { if (view == null) { diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 26739b3..192eded 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -135,6 +135,16 @@ public interface WindowManagerPolicy { */ public interface WindowState { /** + * Return the uid of the app that owns this window. + */ + int getOwningUid(); + + /** + * Return the package name of the app that owns this window. + */ + String getOwningPackage(); + + /** * Perform standard frame computation. The result can be obtained with * getFrame() if so desired. Must be called with the window manager * lock held. @@ -401,61 +411,19 @@ public interface WindowManagerPolicy { public void rebootSafeMode(boolean confirm); } - /** - * Bit mask that is set for all enter transition. - */ - public final int TRANSIT_ENTER_MASK = 0x1000; - - /** - * Bit mask that is set for all exit transitions. - */ - public final int TRANSIT_EXIT_MASK = 0x2000; - - /** Not set up for a transition. */ - public final int TRANSIT_UNSET = -1; - /** No animation for transition. */ - public final int TRANSIT_NONE = 0; /** Window has been added to the screen. */ - public final int TRANSIT_ENTER = 1 | TRANSIT_ENTER_MASK; + public static final int TRANSIT_ENTER = 1; /** Window has been removed from the screen. */ - public final int TRANSIT_EXIT = 2 | TRANSIT_EXIT_MASK; + public static final int TRANSIT_EXIT = 2; /** Window has been made visible. */ - public final int TRANSIT_SHOW = 3 | TRANSIT_ENTER_MASK; - /** Window has been made invisible. */ - public final int TRANSIT_HIDE = 4 | TRANSIT_EXIT_MASK; + public static final int TRANSIT_SHOW = 3; + /** Window has been made invisible. + * TODO: Consider removal as this is unused. */ + public static final int TRANSIT_HIDE = 4; /** The "application starting" preview window is no longer needed, and will * animate away to show the real window. */ - public final int TRANSIT_PREVIEW_DONE = 5; - /** A window in a new activity is being opened on top of an existing one - * in the same task. */ - public final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK; - /** The window in the top-most activity is being closed to reveal the - * previous activity in the same task. */ - public final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK; - /** A window in a new task is being opened on top of an existing one - * in another activity's task. */ - public final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK; - /** A window in the top-most activity is being closed to reveal the - * previous activity in a different task. */ - public final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK; - /** A window in an existing task is being displayed on top of an existing one - * in another activity's task. */ - public final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK; - /** A window in an existing task is being put below all other tasks. */ - public final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK; - /** A window in a new activity that doesn't have a wallpaper is being - * opened on top of one that does, effectively closing the wallpaper. */ - public final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK; - /** A window in a new activity that does have a wallpaper is being - * opened on one that didn't, effectively opening the wallpaper. */ - public final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK; - /** A window in a new activity is being opened on top of an existing one, - * and both are on top of the wallpaper. */ - public final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK; - /** The window in the top-most activity is being closed to reveal the - * previous activity, and both are on top of he wallpaper. */ - public final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK; - + public static final int TRANSIT_PREVIEW_DONE = 5; + // NOTE: screen off reasons are in order of significance, with more // important ones lower than less important ones. @@ -490,15 +458,23 @@ public interface WindowManagerPolicy { public void setInitialDisplaySize(Display display, int width, int height, int density); /** + * Called by window manager to set the overscan region that should be used for the + * given display. + */ + public void setDisplayOverscan(Display display, int left, int top, int right, int bottom); + + /** * Check permissions when adding a window. * - * @param attrs The window's LayoutParams. + * @param attrs The window's LayoutParams. + * @param outAppOp First element will be filled with the app op corresponding to + * this window, or OP_NONE. * * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed; * else an error code, usually * {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add. */ - public int checkAddPermission(WindowManager.LayoutParams attrs); + public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp); /** * Check permissions when adding a window. @@ -1077,6 +1053,16 @@ public interface WindowManagerPolicy { public void keepScreenOnStoppedLw(); /** + * Gets the current user rotation mode. + * + * @return The rotation mode. + * + * @see WindowManagerPolicy#USER_ROTATION_LOCKED + * @see WindowManagerPolicy#USER_ROTATION_FREE + */ + public int getUserRotationMode(); + + /** * Inform the policy that the user has chosen a preferred orientation ("rotation lock"). * * @param mode One of {@link WindowManagerPolicy#USER_ROTATION_LOCKED} or @@ -1112,14 +1098,6 @@ public interface WindowManagerPolicy { public void setLastInputMethodWindowLw(WindowState ime, WindowState target); /** - * Returns whether magnification can be applied to the given window type. - * - * @param attrs The window's LayoutParams. - * @return Whether magnification can be applied. - */ - public boolean canMagnifyWindowLw(WindowManager.LayoutParams attrs); - - /** * Called when the current user changes. Guaranteed to be called before the broadcast * of the new user id is made to all listeners. * @@ -1142,4 +1120,23 @@ public interface WindowManagerPolicy { * {@link android.content.Intent#ACTION_ASSIST} */ public void showAssistant(); + + /** + * Returns whether a given window type can be magnified. + * + * @param windowType The window type. + * @return True if the window can be magnified. + */ + public boolean canMagnifyWindow(int windowType); + + /** + * Returns whether a given window type is considered a top level one. + * A top level window does not have a container, i.e. attached window, + * or if it has a container it is laid out as a top-level window, not + * as a child of its container. + * + * @param windowType The window type. + * @return True if the window is a top level one. + */ + public boolean isTopLevelWindow(int windowType); } diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java deleted file mode 100644 index bf77c67..0000000 --- a/core/java/android/view/WindowOrientationListener.java +++ /dev/null @@ -1,715 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.SystemProperties; -import android.util.FloatMath; -import android.util.Log; -import android.util.Slog; - -/** - * A special helper class used by the WindowManager - * for receiving notifications from the SensorManager when - * the orientation of the device has changed. - * - * NOTE: If changing anything here, please run the API demo - * "App/Activity/Screen Orientation" to ensure that all orientation - * modes still work correctly. - * - * You can also visualize the behavior of the WindowOrientationListener. - * Refer to frameworks/base/tools/orientationplot/README.txt for details. - * - * @hide - */ -public abstract class WindowOrientationListener { - private static final String TAG = "WindowOrientationListener"; - private static final boolean LOG = SystemProperties.getBoolean( - "debug.orientation.log", false); - - private static final boolean USE_GRAVITY_SENSOR = false; - - private SensorManager mSensorManager; - private boolean mEnabled; - private int mRate; - private Sensor mSensor; - private SensorEventListenerImpl mSensorEventListener; - int mCurrentRotation = -1; - - /** - * Creates a new WindowOrientationListener. - * - * @param context for the WindowOrientationListener. - */ - public WindowOrientationListener(Context context) { - this(context, SensorManager.SENSOR_DELAY_UI); - } - - /** - * Creates a new WindowOrientationListener. - * - * @param context for the WindowOrientationListener. - * @param rate at which sensor events are processed (see also - * {@link android.hardware.SensorManager SensorManager}). Use the default - * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL - * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. - * - * This constructor is private since no one uses it. - */ - private WindowOrientationListener(Context context, int rate) { - mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); - mRate = rate; - mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR - ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); - if (mSensor != null) { - // Create listener only if sensors do exist - mSensorEventListener = new SensorEventListenerImpl(this); - } - } - - /** - * Enables the WindowOrientationListener so it will monitor the sensor and call - * {@link #onOrientationChanged} when the device orientation changes. - */ - public void enable() { - if (mSensor == null) { - Log.w(TAG, "Cannot detect sensors. Not enabled"); - return; - } - if (mEnabled == false) { - if (LOG) { - Log.d(TAG, "WindowOrientationListener enabled"); - } - mSensorEventListener.reset(); - mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); - mEnabled = true; - } - } - - /** - * Disables the WindowOrientationListener. - */ - public void disable() { - if (mSensor == null) { - Log.w(TAG, "Cannot detect sensors. Invalid disable"); - return; - } - if (mEnabled == true) { - if (LOG) { - Log.d(TAG, "WindowOrientationListener disabled"); - } - mSensorManager.unregisterListener(mSensorEventListener); - mEnabled = false; - } - } - - /** - * Sets the current rotation. - * - * @param rotation The current rotation. - */ - public void setCurrentRotation(int rotation) { - mCurrentRotation = rotation; - } - - /** - * Gets the proposed rotation. - * - * This method only returns a rotation if the orientation listener is certain - * of its proposal. If the rotation is indeterminate, returns -1. - * - * @return The proposed rotation, or -1 if unknown. - */ - public int getProposedRotation() { - if (mEnabled) { - return mSensorEventListener.getProposedRotation(); - } - return -1; - } - - /** - * Returns true if sensor is enabled and false otherwise - */ - public boolean canDetectOrientation() { - return mSensor != null; - } - - /** - * Called when the rotation view of the device has changed. - * - * This method is called whenever the orientation becomes certain of an orientation. - * It is called each time the orientation determination transitions from being - * uncertain to being certain again, even if it is the same orientation as before. - * - * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. - * @see Surface - */ - public abstract void onProposedRotationChanged(int rotation); - - /** - * This class filters the raw accelerometer data and tries to detect actual changes in - * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, - * but here's the outline: - * - * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in - * cartesian space because the orientation calculations are sensitive to the - * absolute magnitude of the acceleration. In particular, there are singularities - * in the calculation as the magnitude approaches 0. By performing the low-pass - * filtering early, we can eliminate most spurious high-frequency impulses due to noise. - * - * - Convert the acceleromter vector from cartesian to spherical coordinates. - * Since we're dealing with rotation of the device, this is the sensible coordinate - * system to work in. The zenith direction is the Z-axis, the direction the screen - * is facing. The radial distance is referred to as the magnitude below. - * The elevation angle is referred to as the "tilt" below. - * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is - * the Y-axis). - * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference. - * - * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing. - * The orientation angle is not meaningful when the device is nearly horizontal. - * The tilt angle thresholds are set differently for each orientation and different - * limits are applied when the device is facing down as opposed to when it is facing - * forward or facing up. - * - * - When the orientation angle reaches a certain threshold, consider transitioning - * to the corresponding orientation. These thresholds have some hysteresis built-in - * to avoid oscillations between adjacent orientations. - * - * - Wait for the device to settle for a little bit. Once that happens, issue the - * new orientation proposal. - * - * Details are explained inline. - * - * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for - * signal processing background. - */ - static final class SensorEventListenerImpl implements SensorEventListener { - // We work with all angles in degrees in this class. - private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); - - // Number of nanoseconds per millisecond. - private static final long NANOS_PER_MS = 1000000; - - // Indices into SensorEvent.values for the accelerometer sensor. - private static final int ACCELEROMETER_DATA_X = 0; - private static final int ACCELEROMETER_DATA_Y = 1; - private static final int ACCELEROMETER_DATA_Z = 2; - - private final WindowOrientationListener mOrientationListener; - - // The minimum amount of time that a predicted rotation must be stable before it - // is accepted as a valid rotation proposal. This value can be quite small because - // the low-pass filter already suppresses most of the noise so we're really just - // looking for quick confirmation that the last few samples are in agreement as to - // the desired orientation. - private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS; - - // The minimum amount of time that must have elapsed since the device last exited - // the flat state (time since it was picked up) before the proposed rotation - // can change. - private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS; - - // The minimum amount of time that must have elapsed since the device stopped - // swinging (time since device appeared to be in the process of being put down - // or put away into a pocket) before the proposed rotation can change. - private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS; - - // The minimum amount of time that must have elapsed since the device stopped - // undergoing external acceleration before the proposed rotation can change. - private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS = - 500 * NANOS_PER_MS; - - // If the tilt angle remains greater than the specified angle for a minimum of - // the specified time, then the device is deemed to be lying flat - // (just chillin' on a table). - private static final float FLAT_ANGLE = 75; - private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS; - - // If the tilt angle has increased by at least delta degrees within the specified amount - // of time, then the device is deemed to be swinging away from the user - // down towards flat (tilt = 90). - private static final float SWING_AWAY_ANGLE_DELTA = 20; - private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS; - - // The maximum sample inter-arrival time in milliseconds. - // If the acceleration samples are further apart than this amount in time, we reset the - // state of the low-pass filter and orientation properties. This helps to handle - // boundary conditions when the device is turned on, wakes from suspend or there is - // a significant gap in samples. - private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS; - - // The acceleration filter time constant. - // - // This time constant is used to tune the acceleration filter such that - // impulses and vibrational noise (think car dock) is suppressed before we - // try to calculate the tilt and orientation angles. - // - // The filter time constant is related to the filter cutoff frequency, which is the - // frequency at which signals are attenuated by 3dB (half the passband power). - // Each successive octave beyond this frequency is attenuated by an additional 6dB. - // - // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz - // is given by Fc = 1 / (2pi * t). - // - // The higher the time constant, the lower the cutoff frequency, so more noise - // will be suppressed. - // - // Filtering adds latency proportional the time constant (inversely proportional - // to the cutoff frequency) so we don't want to make the time constant too - // large or we can lose responsiveness. Likewise we don't want to make it too - // small or we do a poor job suppressing acceleration spikes. - // Empirically, 100ms seems to be too small and 500ms is too large. - private static final float FILTER_TIME_CONSTANT_MS = 200.0f; - - /* State for orientation detection. */ - - // Thresholds for minimum and maximum allowable deviation from gravity. - // - // If the device is undergoing external acceleration (being bumped, in a car - // that is turning around a corner or a plane taking off) then the magnitude - // may be substantially more or less than gravity. This can skew our orientation - // detection by making us think that up is pointed in a different direction. - // - // Conversely, if the device is in freefall, then there will be no gravity to - // measure at all. This is problematic because we cannot detect the orientation - // without gravity to tell us which way is up. A magnitude near 0 produces - // singularities in the tilt and orientation calculations. - // - // In both cases, we postpone choosing an orientation. - // - // However, we need to tolerate some acceleration because the angular momentum - // of turning the device can skew the observed acceleration for a short period of time. - private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2 - private static final float ACCELERATION_TOLERANCE = 4; // m/s^2 - private static final float MIN_ACCELERATION_MAGNITUDE = - SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE; - private static final float MAX_ACCELERATION_MAGNITUDE = - SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE; - - // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e. - // when screen is facing the sky or ground), we completely ignore orientation data. - private static final int MAX_TILT = 75; - - // The tilt angle range in degrees for each orientation. - // Beyond these tilt angles, we don't even consider transitioning into the - // specified orientation. We place more stringent requirements on unnatural - // orientations than natural ones to make it less likely to accidentally transition - // into those states. - // The first value of each pair is negative so it applies a limit when the device is - // facing down (overhead reading in bed). - // The second value of each pair is positive so it applies a limit when the device is - // facing up (resting on a table). - // The ideal tilt angle is 0 (when the device is vertical) so the limits establish - // how close to vertical the device must be in order to change orientation. - private static final int[][] TILT_TOLERANCE = new int[][] { - /* ROTATION_0 */ { -25, 70 }, - /* ROTATION_90 */ { -25, 65 }, - /* ROTATION_180 */ { -25, 60 }, - /* ROTATION_270 */ { -25, 65 } - }; - - // The gap angle in degrees between adjacent orientation angles for hysteresis. - // This creates a "dead zone" between the current orientation and a proposed - // adjacent orientation. No orientation proposal is made when the orientation - // angle is within the gap between the current orientation and the adjacent - // orientation. - private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45; - - // Timestamp and value of the last accelerometer sample. - private long mLastFilteredTimestampNanos; - private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; - - // The last proposed rotation, -1 if unknown. - private int mProposedRotation; - - // Value of the current predicted rotation, -1 if unknown. - private int mPredictedRotation; - - // Timestamp of when the predicted rotation most recently changed. - private long mPredictedRotationTimestampNanos; - - // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). - private long mFlatTimestampNanos; - - // Timestamp when the device last appeared to be swinging. - private long mSwingTimestampNanos; - - // Timestamp when the device last appeared to be undergoing external acceleration. - private long mAccelerationTimestampNanos; - - // History of observed tilt angles. - private static final int TILT_HISTORY_SIZE = 40; - private float[] mTiltHistory = new float[TILT_HISTORY_SIZE]; - private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE]; - private int mTiltHistoryIndex; - - public SensorEventListenerImpl(WindowOrientationListener orientationListener) { - mOrientationListener = orientationListener; - reset(); - } - - public int getProposedRotation() { - return mProposedRotation; - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - - @Override - public void onSensorChanged(SensorEvent event) { - // The vector given in the SensorEvent points straight up (towards the sky) under ideal - // conditions (the phone is not accelerating). I'll call this up vector elsewhere. - float x = event.values[ACCELEROMETER_DATA_X]; - float y = event.values[ACCELEROMETER_DATA_Y]; - float z = event.values[ACCELEROMETER_DATA_Z]; - - if (LOG) { - Slog.v(TAG, "Raw acceleration vector: " - + "x=" + x + ", y=" + y + ", z=" + z - + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); - } - - // Apply a low-pass filter to the acceleration up vector in cartesian space. - // Reset the orientation listener state if the samples are too far apart in time - // or when we see values of (0, 0, 0) which indicates that we polled the - // accelerometer too soon after turning it on and we don't have any data yet. - final long now = event.timestamp; - final long then = mLastFilteredTimestampNanos; - final float timeDeltaMS = (now - then) * 0.000001f; - final boolean skipSample; - if (now < then - || now > then + MAX_FILTER_DELTA_TIME_NANOS - || (x == 0 && y == 0 && z == 0)) { - if (LOG) { - Slog.v(TAG, "Resetting orientation listener."); - } - reset(); - skipSample = true; - } else { - final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); - x = alpha * (x - mLastFilteredX) + mLastFilteredX; - y = alpha * (y - mLastFilteredY) + mLastFilteredY; - z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; - if (LOG) { - Slog.v(TAG, "Filtered acceleration vector: " - + "x=" + x + ", y=" + y + ", z=" + z - + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); - } - skipSample = false; - } - mLastFilteredTimestampNanos = now; - mLastFilteredX = x; - mLastFilteredY = y; - mLastFilteredZ = z; - - boolean isAccelerating = false; - boolean isFlat = false; - boolean isSwinging = false; - if (!skipSample) { - // Calculate the magnitude of the acceleration vector. - final float magnitude = FloatMath.sqrt(x * x + y * y + z * z); - if (magnitude < NEAR_ZERO_MAGNITUDE) { - if (LOG) { - Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero."); - } - clearPredictedRotation(); - } else { - // Determine whether the device appears to be undergoing external acceleration. - if (isAccelerating(magnitude)) { - isAccelerating = true; - mAccelerationTimestampNanos = now; - } - - // Calculate the tilt angle. - // This is the angle between the up vector and the x-y plane (the plane of - // the screen) in a range of [-90, 90] degrees. - // -90 degrees: screen horizontal and facing the ground (overhead) - // 0 degrees: screen vertical - // 90 degrees: screen horizontal and facing the sky (on table) - final int tiltAngle = (int) Math.round( - Math.asin(z / magnitude) * RADIANS_TO_DEGREES); - addTiltHistoryEntry(now, tiltAngle); - - // Determine whether the device appears to be flat or swinging. - if (isFlat(now)) { - isFlat = true; - mFlatTimestampNanos = now; - } - if (isSwinging(now, tiltAngle)) { - isSwinging = true; - mSwingTimestampNanos = now; - } - - // If the tilt angle is too close to horizontal then we cannot determine - // the orientation angle of the screen. - if (Math.abs(tiltAngle) > MAX_TILT) { - if (LOG) { - Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " - + "tiltAngle=" + tiltAngle); - } - clearPredictedRotation(); - } else { - // Calculate the orientation angle. - // This is the angle between the x-y projection of the up vector onto - // the +y-axis, increasing clockwise in a range of [0, 360] degrees. - int orientationAngle = (int) Math.round( - -Math.atan2(-x, y) * RADIANS_TO_DEGREES); - if (orientationAngle < 0) { - // atan2 returns [-180, 180]; normalize to [0, 360] - orientationAngle += 360; - } - - // Find the nearest rotation. - int nearestRotation = (orientationAngle + 45) / 90; - if (nearestRotation == 4) { - nearestRotation = 0; - } - - // Determine the predicted orientation. - if (isTiltAngleAcceptable(nearestRotation, tiltAngle) - && isOrientationAngleAcceptable(nearestRotation, - orientationAngle)) { - updatePredictedRotation(now, nearestRotation); - if (LOG) { - Slog.v(TAG, "Predicted: " - + "tiltAngle=" + tiltAngle - + ", orientationAngle=" + orientationAngle - + ", predictedRotation=" + mPredictedRotation - + ", predictedRotationAgeMS=" - + ((now - mPredictedRotationTimestampNanos) - * 0.000001f)); - } - } else { - if (LOG) { - Slog.v(TAG, "Ignoring sensor data, no predicted rotation: " - + "tiltAngle=" + tiltAngle - + ", orientationAngle=" + orientationAngle); - } - clearPredictedRotation(); - } - } - } - } - - // Determine new proposed rotation. - final int oldProposedRotation = mProposedRotation; - if (mPredictedRotation < 0 || isPredictedRotationAcceptable(now)) { - mProposedRotation = mPredictedRotation; - } - - // Write final statistics about where we are in the orientation detection process. - if (LOG) { - Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation - + ", proposedRotation=" + mProposedRotation - + ", predictedRotation=" + mPredictedRotation - + ", timeDeltaMS=" + timeDeltaMS - + ", isAccelerating=" + isAccelerating - + ", isFlat=" + isFlat - + ", isSwinging=" + isSwinging - + ", timeUntilSettledMS=" + remainingMS(now, - mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) - + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, - mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) - + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now, - mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) - + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now, - mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)); - } - - // Tell the listener. - if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) { - if (LOG) { - Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + mProposedRotation - + ", oldProposedRotation=" + oldProposedRotation); - } - mOrientationListener.onProposedRotationChanged(mProposedRotation); - } - } - - /** - * Returns true if the tilt angle is acceptable for a given predicted rotation. - */ - private boolean isTiltAngleAcceptable(int rotation, int tiltAngle) { - return tiltAngle >= TILT_TOLERANCE[rotation][0] - && tiltAngle <= TILT_TOLERANCE[rotation][1]; - } - - /** - * Returns true if the orientation angle is acceptable for a given predicted rotation. - * - * This function takes into account the gap between adjacent orientations - * for hysteresis. - */ - private boolean isOrientationAngleAcceptable(int rotation, int orientationAngle) { - // If there is no current rotation, then there is no gap. - // The gap is used only to introduce hysteresis among advertised orientation - // changes to avoid flapping. - final int currentRotation = mOrientationListener.mCurrentRotation; - if (currentRotation >= 0) { - // If the specified rotation is the same or is counter-clockwise adjacent - // to the current rotation, then we set a lower bound on the orientation angle. - // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90, - // then we want to check orientationAngle > 45 + GAP / 2. - if (rotation == currentRotation - || rotation == (currentRotation + 1) % 4) { - int lowerBound = rotation * 90 - 45 - + ADJACENT_ORIENTATION_ANGLE_GAP / 2; - if (rotation == 0) { - if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) { - return false; - } - } else { - if (orientationAngle < lowerBound) { - return false; - } - } - } - - // If the specified rotation is the same or is clockwise adjacent, - // then we set an upper bound on the orientation angle. - // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270, - // then we want to check orientationAngle < 315 - GAP / 2. - if (rotation == currentRotation - || rotation == (currentRotation + 3) % 4) { - int upperBound = rotation * 90 + 45 - - ADJACENT_ORIENTATION_ANGLE_GAP / 2; - if (rotation == 0) { - if (orientationAngle <= 45 && orientationAngle > upperBound) { - return false; - } - } else { - if (orientationAngle > upperBound) { - return false; - } - } - } - } - return true; - } - - /** - * Returns true if the predicted rotation is ready to be advertised as a - * proposed rotation. - */ - private boolean isPredictedRotationAcceptable(long now) { - // The predicted rotation must have settled long enough. - if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) { - return false; - } - - // The last flat state (time since picked up) must have been sufficiently long ago. - if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) { - return false; - } - - // The last swing state (time since last movement to put down) must have been - // sufficiently long ago. - if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) { - return false; - } - - // The last acceleration state must have been sufficiently long ago. - if (now < mAccelerationTimestampNanos - + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) { - return false; - } - - // Looks good! - return true; - } - - private void reset() { - mLastFilteredTimestampNanos = Long.MIN_VALUE; - mProposedRotation = -1; - mFlatTimestampNanos = Long.MIN_VALUE; - mSwingTimestampNanos = Long.MIN_VALUE; - mAccelerationTimestampNanos = Long.MIN_VALUE; - clearPredictedRotation(); - clearTiltHistory(); - } - - private void clearPredictedRotation() { - mPredictedRotation = -1; - mPredictedRotationTimestampNanos = Long.MIN_VALUE; - } - - private void updatePredictedRotation(long now, int rotation) { - if (mPredictedRotation != rotation) { - mPredictedRotation = rotation; - mPredictedRotationTimestampNanos = now; - } - } - - private boolean isAccelerating(float magnitude) { - return magnitude < MIN_ACCELERATION_MAGNITUDE - || magnitude > MAX_ACCELERATION_MAGNITUDE; - } - - private void clearTiltHistory() { - mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE; - mTiltHistoryIndex = 1; - } - - private void addTiltHistoryEntry(long now, float tilt) { - mTiltHistory[mTiltHistoryIndex] = tilt; - mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now; - mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE; - mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE; - } - - private boolean isFlat(long now) { - for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndex(i)) >= 0; ) { - if (mTiltHistory[i] < FLAT_ANGLE) { - break; - } - if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) { - // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS. - return true; - } - } - return false; - } - - private boolean isSwinging(long now, float tilt) { - for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndex(i)) >= 0; ) { - if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) { - break; - } - if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) { - // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS. - return true; - } - } - return false; - } - - private int nextTiltHistoryIndex(int index) { - index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1; - return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; - } - - private static float remainingMS(long now, long until) { - return now >= until ? 0 : (until - now) * 0.000001f; - } - } -} diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 1500905..9603fe5 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -19,6 +19,7 @@ package android.view.accessibility; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.Pools.SynchronizedPool; import java.util.ArrayList; import java.util.List; @@ -686,11 +687,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPES_ALL_MASK = 0xFFFFFFFF; private static final int MAX_POOL_SIZE = 10; - private static final Object sPoolLock = new Object(); - private static AccessibilityEvent sPool; - private static int sPoolSize; - private AccessibilityEvent mNext; - private boolean mIsInPool; + private static final SynchronizedPool<AccessibilityEvent> sPool = + new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE); private int mEventType; private CharSequence mPackageName; @@ -916,17 +914,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @return An instance. */ public static AccessibilityEvent obtain() { - synchronized (sPoolLock) { - if (sPool != null) { - AccessibilityEvent event = sPool; - sPool = sPool.mNext; - sPoolSize--; - event.mNext = null; - event.mIsInPool = false; - return event; - } - return new AccessibilityEvent(); - } + AccessibilityEvent event = sPool.acquire(); + return (event != null) ? event : new AccessibilityEvent(); } /** @@ -939,18 +928,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par */ @Override public void recycle() { - if (mIsInPool) { - throw new IllegalStateException("Event already recycled!"); - } clear(); - synchronized (sPoolLock) { - if (sPoolSize <= MAX_POOL_SIZE) { - mNext = sPool; - sPool = this; - mIsInPool = true; - sPoolSize++; - } - } + sPool.release(this); } /** @@ -1137,54 +1116,176 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @return The string representation. */ public static String eventTypeToString(int eventType) { - switch (eventType) { - case TYPE_VIEW_CLICKED: - return "TYPE_VIEW_CLICKED"; - case TYPE_VIEW_LONG_CLICKED: - return "TYPE_VIEW_LONG_CLICKED"; - case TYPE_VIEW_SELECTED: - return "TYPE_VIEW_SELECTED"; - case TYPE_VIEW_FOCUSED: - return "TYPE_VIEW_FOCUSED"; - case TYPE_VIEW_TEXT_CHANGED: - return "TYPE_VIEW_TEXT_CHANGED"; - case TYPE_WINDOW_STATE_CHANGED: - return "TYPE_WINDOW_STATE_CHANGED"; - case TYPE_VIEW_HOVER_ENTER: - return "TYPE_VIEW_HOVER_ENTER"; - case TYPE_VIEW_HOVER_EXIT: - return "TYPE_VIEW_HOVER_EXIT"; - case TYPE_NOTIFICATION_STATE_CHANGED: - return "TYPE_NOTIFICATION_STATE_CHANGED"; - case TYPE_TOUCH_EXPLORATION_GESTURE_START: - return "TYPE_TOUCH_EXPLORATION_GESTURE_START"; - case TYPE_TOUCH_EXPLORATION_GESTURE_END: - return "TYPE_TOUCH_EXPLORATION_GESTURE_END"; - case TYPE_WINDOW_CONTENT_CHANGED: - return "TYPE_WINDOW_CONTENT_CHANGED"; - case TYPE_VIEW_TEXT_SELECTION_CHANGED: - return "TYPE_VIEW_TEXT_SELECTION_CHANGED"; - case TYPE_VIEW_SCROLLED: - return "TYPE_VIEW_SCROLLED"; - case TYPE_ANNOUNCEMENT: - return "TYPE_ANNOUNCEMENT"; - case TYPE_VIEW_ACCESSIBILITY_FOCUSED: - return "TYPE_VIEW_ACCESSIBILITY_FOCUSED"; - case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: - return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED"; - case TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: - return "TYPE_CURRENT_AT_GRANULARITY_MOVEMENT_CHANGED"; - case TYPE_GESTURE_DETECTION_START: - return "TYPE_GESTURE_DETECTION_START"; - case TYPE_GESTURE_DETECTION_END: - return "TYPE_GESTURE_DETECTION_END"; - case TYPE_TOUCH_INTERACTION_START: - return "TYPE_TOUCH_INTERACTION_START"; - case TYPE_TOUCH_INTERACTION_END: - return "TYPE_TOUCH_INTERACTION_END"; - default: - return null; + if (eventType == TYPES_ALL_MASK) { + return "TYPES_ALL_MASK"; } + StringBuilder builder = new StringBuilder(); + int eventTypeCount = 0; + while (eventType != 0) { + final int eventTypeFlag = 1 << Integer.numberOfTrailingZeros(eventType); + eventType &= ~eventTypeFlag; + switch (eventTypeFlag) { + case TYPE_VIEW_CLICKED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_CLICKED"); + eventTypeCount++; + } break; + case TYPE_VIEW_LONG_CLICKED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_LONG_CLICKED"); + eventTypeCount++; + } break; + case TYPE_VIEW_SELECTED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_SELECTED"); + eventTypeCount++; + } break; + case TYPE_VIEW_FOCUSED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_FOCUSED"); + eventTypeCount++; + } break; + case TYPE_VIEW_TEXT_CHANGED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_TEXT_CHANGED"); + eventTypeCount++; + } break; + case TYPE_WINDOW_STATE_CHANGED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_WINDOW_STATE_CHANGED"); + eventTypeCount++; + } break; + case TYPE_VIEW_HOVER_ENTER: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_HOVER_ENTER"); + eventTypeCount++; + } break; + case TYPE_VIEW_HOVER_EXIT: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_HOVER_EXIT"); + eventTypeCount++; + } break; + case TYPE_NOTIFICATION_STATE_CHANGED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_NOTIFICATION_STATE_CHANGED"); + eventTypeCount++; + } break; + case TYPE_TOUCH_EXPLORATION_GESTURE_START: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_TOUCH_EXPLORATION_GESTURE_START"); + eventTypeCount++; + } break; + case TYPE_TOUCH_EXPLORATION_GESTURE_END: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_TOUCH_EXPLORATION_GESTURE_END"); + eventTypeCount++; + } break; + case TYPE_WINDOW_CONTENT_CHANGED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_WINDOW_CONTENT_CHANGED"); + eventTypeCount++; + } break; + case TYPE_VIEW_TEXT_SELECTION_CHANGED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_TEXT_SELECTION_CHANGED"); + eventTypeCount++; + } break; + case TYPE_VIEW_SCROLLED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_SCROLLED"); + eventTypeCount++; + } break; + case TYPE_ANNOUNCEMENT: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_ANNOUNCEMENT"); + eventTypeCount++; + } break; + case TYPE_VIEW_ACCESSIBILITY_FOCUSED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_ACCESSIBILITY_FOCUSED"); + eventTypeCount++; + } break; + case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED"); + eventTypeCount++; + } break; + case TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_CURRENT_AT_GRANULARITY_MOVEMENT_CHANGED"); + eventTypeCount++; + } break; + case TYPE_GESTURE_DETECTION_START: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_GESTURE_DETECTION_START"); + eventTypeCount++; + } break; + case TYPE_GESTURE_DETECTION_END: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_GESTURE_DETECTION_END"); + eventTypeCount++; + } break; + case TYPE_TOUCH_INTERACTION_START: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_TOUCH_INTERACTION_START"); + eventTypeCount++; + } break; + case TYPE_TOUCH_INTERACTION_END: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_TOUCH_INTERACTION_END"); + eventTypeCount++; + } break; + } + } + if (eventTypeCount > 1) { + builder.insert(0, '['); + builder.append(']'); + } + return builder.toString(); } /** diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 20b5f17..84d7e72 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -17,7 +17,6 @@ package android.view.accessibility; import android.accessibilityservice.IAccessibilityServiceConnection; -import android.graphics.Rect; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -102,8 +101,6 @@ public final class AccessibilityInteractionClient private Message mSameThreadMessage; - private final Rect mTempBounds = new Rect(); - // The connection cache is shared between all interrogating threads. private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = new SparseArray<IAccessibilityServiceConnection>(); @@ -194,14 +191,14 @@ public final class AccessibilityInteractionClient return cachedInfo; } final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( + final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId( accessibilityWindowId, accessibilityNodeId, interactionId, this, prefetchFlags, Thread.currentThread().getId()); // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); if (infos != null && !infos.isEmpty()) { return infos.get(0); } @@ -233,25 +230,25 @@ public final class AccessibilityInteractionClient * where to start the search. Use * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. - * @param viewId The id of the view. - * @return An {@link AccessibilityNodeInfo} if found, null otherwise. + * @param viewId The fully qualified resource name of the view id to find. + * @return An list of {@link AccessibilityNodeInfo} if found, empty list otherwise. */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId, - int accessibilityWindowId, long accessibilityNodeId, int viewId) { + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(int connectionId, + int accessibilityWindowId, long accessibilityNodeId, String viewId) { try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = - connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId, - accessibilityNodeId, viewId, interactionId, this, - Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { - AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( + final boolean success = connection.findAccessibilityNodeInfosByViewId( + accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, + Thread.currentThread().getId()); + if (success) { + List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); - return info; + if (infos != null) { + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); + return infos; + } } } else { if (DEBUG) { @@ -264,7 +261,7 @@ public final class AccessibilityInteractionClient + " findAccessibilityNodeInfoByViewIdInActiveWindow", re); } } - return null; + return Collections.emptyList(); } /** @@ -290,15 +287,16 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfosByText( + final boolean success = connection.findAccessibilityNodeInfosByText( accessibilityWindowId, accessibilityNodeId, text, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); - return infos; + if (infos != null) { + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); + return infos; + } } } else { if (DEBUG) { @@ -336,14 +334,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findFocus(accessibilityWindowId, + final boolean success = connection.findFocus(accessibilityWindowId, accessibilityNodeId, focusType, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); return info; } } else { @@ -381,14 +378,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.focusSearch(accessibilityWindowId, + final boolean success = connection.focusSearch(accessibilityWindowId, accessibilityNodeId, direction, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); return info; } } else { @@ -604,36 +600,14 @@ public final class AccessibilityInteractionClient } /** - * Applies compatibility scale to the info bounds if it is not equal to one. - * - * @param info The info whose bounds to scale. - * @param scale The scale to apply. - */ - private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) { - if (scale == 1.0f) { - return; - } - Rect bounds = mTempBounds; - info.getBoundsInParent(bounds); - bounds.scale(scale); - info.setBoundsInParent(bounds); - - info.getBoundsInScreen(bounds); - bounds.scale(scale); - info.setBoundsInScreen(bounds); - } - - /** * Finalize an {@link AccessibilityNodeInfo} before passing it to the client. * * @param info The info. * @param connectionId The id of the connection to the system. - * @param windowScale The source window compatibility scale. */ - private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, - float windowScale) { + private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, + int connectionId) { if (info != null) { - applyCompatibilityScaleIfNeeded(info, windowScale); info.setConnectionId(connectionId); info.setSealed(true); sAccessibilityNodeInfoCache.add(info); @@ -645,15 +619,14 @@ public final class AccessibilityInteractionClient * * @param infos The {@link AccessibilityNodeInfo}s. * @param connectionId The id of the connection to the system. - * @param windowScale The source window compatibility scale. */ private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, - int connectionId, float windowScale) { + int connectionId) { if (infos != null) { final int infosCount = infos.size(); for (int i = 0; i < infosCount; i++) { AccessibilityNodeInfo info = infos.get(i); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); } } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 1dc2487..ad87fcb 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -16,10 +16,12 @@ package android.view.accessibility; +import android.accessibilityservice.AccessibilityServiceInfo; import android.graphics.Rect; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pools.SynchronizedPool; import android.util.SparseLongArray; import android.view.View; @@ -77,7 +79,10 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; /** @hide */ - public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; + public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; + + /** @hide */ + public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; // Actions. @@ -126,16 +131,22 @@ public class AccessibilityNodeInfo implements Parcelable { * at a given movement granularity. For example, move to the next character, * word, etc. * <p> - * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br> - * <strong>Example:</strong> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, + * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> + * <strong>Example:</strong> Move to the previous character and do not extend selection. * <code><pre><p> * Bundle arguments = new Bundle(); * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); + * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, + * false); * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments); * </code></pre></p> * </p> * + * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * * @see #setMovementGranularities(int) * @see #getMovementGranularities() * @@ -152,17 +163,23 @@ public class AccessibilityNodeInfo implements Parcelable { * at a given movement granularity. For example, move to the next character, * word, etc. * <p> - * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br> - * <strong>Example:</strong> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, + * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> + * <strong>Example:</strong> Move to the next character and do not extend selection. * <code><pre><p> * Bundle arguments = new Bundle(); * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); + * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, + * false); * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, * arguments); * </code></pre></p> * </p> * + * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * * @see #setMovementGranularities(int) * @see #getMovementGranularities() * @@ -215,15 +232,53 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_SCROLL_BACKWARD = 0x00002000; /** + * Action to copy the current selection to the clipboard. + */ + public static final int ACTION_COPY = 0x00004000; + + /** + * Action to paste the current clipboard content. + */ + public static final int ACTION_PASTE = 0x00008000; + + /** + * Action to cut the current selection and place it to the clipboard. + */ + public static final int ACTION_CUT = 0x00010000; + + /** + * Action to set the selection. Performing this action with no arguments + * clears the selection. + * <p> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT}, + * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); + * info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments); + * </code></pre></p> + * </p> + * + * @see #ACTION_ARGUMENT_SELECTION_START_INT + * @see #ACTION_ARGUMENT_SELECTION_END_INT + */ + public static final int ACTION_SET_SELECTION = 0x00020000; + + /** * Argument for which movement granularity to be used when traversing the node text. * <p> * <strong>Type:</strong> int<br> * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} * </p> + * + * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY + * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY */ public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = - "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; + "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; /** * Argument for which HTML element to get moving to the next/previous HTML element. @@ -232,9 +287,51 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT}, * {@link #ACTION_PREVIOUS_HTML_ELEMENT} * </p> + * + * @see #ACTION_NEXT_HTML_ELEMENT + * @see #ACTION_PREVIOUS_HTML_ELEMENT */ public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = - "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; + "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; + + /** + * Argument for whether when moving at granularity to extend the selection + * or to move it otherwise. + * <p> + * <strong>Type:</strong> boolean<br> + * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, + * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} + * </p> + * + * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY + * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY + */ + public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = + "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN"; + + /** + * Argument for specifying the selection start. + * <p> + * <strong>Type:</strong> int<br> + * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION} + * </p> + * + * @see #ACTION_SET_SELECTION + */ + public static final String ACTION_ARGUMENT_SELECTION_START_INT = + "ACTION_ARGUMENT_SELECTION_START_INT"; + + /** + * Argument for specifying the selection end. + * <p> + * <strong>Type:</strong> int<br> + * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION} + * </p> + * + * @see #ACTION_SET_SELECTION + */ + public static final String ACTION_ARGUMENT_SELECTION_END_INT = + "ACTION_ARGUMENT_SELECTION_END_INT"; /** * The input focus. @@ -275,29 +372,31 @@ public class AccessibilityNodeInfo implements Parcelable { // Boolean attributes. - private static final int PROPERTY_CHECKABLE = 0x00000001; + private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001; + + private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002; - private static final int PROPERTY_CHECKED = 0x00000002; + private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004; - private static final int PROPERTY_FOCUSABLE = 0x00000004; + private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008; - private static final int PROPERTY_FOCUSED = 0x00000008; + private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010; - private static final int PROPERTY_SELECTED = 0x00000010; + private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020; - private static final int PROPERTY_CLICKABLE = 0x00000020; + private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040; - private static final int PROPERTY_LONG_CLICKABLE = 0x00000040; + private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080; - private static final int PROPERTY_ENABLED = 0x00000080; + private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100; - private static final int PROPERTY_PASSWORD = 0x00000100; + private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200; - private static final int PROPERTY_SCROLLABLE = 0x00000200; + private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; - private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; + private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800; - private static final int PROPERTY_VISIBLE_TO_USER = 0x00000800; + private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000; /** * Bits that provide the id of a virtual descendant of a view. @@ -354,11 +453,9 @@ public class AccessibilityNodeInfo implements Parcelable { // Housekeeping. private static final int MAX_POOL_SIZE = 50; - private static final Object sPoolLock = new Object(); - private static AccessibilityNodeInfo sPool; - private static int sPoolSize; - private AccessibilityNodeInfo mNext; - private boolean mIsInPool; + private static final SynchronizedPool<AccessibilityNodeInfo> sPool = + new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE); + private boolean mSealed; // Data. @@ -376,12 +473,16 @@ public class AccessibilityNodeInfo implements Parcelable { private CharSequence mClassName; private CharSequence mText; private CharSequence mContentDescription; + private CharSequence mViewIdResourceName; private final SparseLongArray mChildNodeIds = new SparseLongArray(); private int mActions; private int mMovementGranularities; + private int mTextSelectionStart = UNDEFINED; + private int mTextSelectionEnd = UNDEFINED; + private int mConnectionId = UNDEFINED; /** @@ -487,6 +588,31 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Refreshes this info with the latest state of the view it represents. + * <p> + * <strong>Note:</strong> If this method returns false this info is obsolete + * since it represents a view that is no longer in the view tree and should + * be recycled. + * </p> + * @return Whether the refresh succeeded. + */ + public boolean refresh() { + enforceSealed(); + if (!canPerformRequestOverConnection(mSourceNodeId)) { + return false; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( + mConnectionId, mWindowId, mSourceNodeId, 0); + if (refreshedInfo == null) { + return false; + } + init(refreshedInfo); + refreshedInfo.recycle(); + return true; + } + + /** * @return The ids of the children. * * @hide @@ -705,6 +831,37 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource + * name where a fully qualified id is of the from "package:id/id_resource_name". + * For example, if the target application's package is "foo.bar" and the id + * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz". + * + * <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> + * <p> + * <strong>Note:</strong> The primary usage of this API is for UI test automation + * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} + * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} + * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. + * </p> + * + * @param viewId The fully qualified resource name of the view id to find. + * @return A list of node info. + */ + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { + enforceSealed(); + if (!canPerformRequestOverConnection(mSourceNodeId)) { + return Collections.emptyList(); + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId, + viewId); + } + + /** * Gets the parent. * <p> * <strong>Note:</strong> It is a client responsibility to recycle the @@ -835,7 +992,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is checkable. */ public boolean isCheckable() { - return getBooleanProperty(PROPERTY_CHECKABLE); + return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE); } /** @@ -851,7 +1008,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setCheckable(boolean checkable) { - setBooleanProperty(PROPERTY_CHECKABLE, checkable); + setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable); } /** @@ -860,7 +1017,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is checked. */ public boolean isChecked() { - return getBooleanProperty(PROPERTY_CHECKED); + return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED); } /** @@ -876,7 +1033,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setChecked(boolean checked) { - setBooleanProperty(PROPERTY_CHECKED, checked); + setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); } /** @@ -885,7 +1042,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is focusable. */ public boolean isFocusable() { - return getBooleanProperty(PROPERTY_FOCUSABLE); + return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE); } /** @@ -901,7 +1058,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setFocusable(boolean focusable) { - setBooleanProperty(PROPERTY_FOCUSABLE, focusable); + setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable); } /** @@ -910,7 +1067,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is focused. */ public boolean isFocused() { - return getBooleanProperty(PROPERTY_FOCUSED); + return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); } /** @@ -926,7 +1083,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setFocused(boolean focused) { - setBooleanProperty(PROPERTY_FOCUSED, focused); + setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); } /** @@ -935,7 +1092,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return Whether the node is visible to the user. */ public boolean isVisibleToUser() { - return getBooleanProperty(PROPERTY_VISIBLE_TO_USER); + return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER); } /** @@ -951,7 +1108,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setVisibleToUser(boolean visibleToUser) { - setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser); + setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser); } /** @@ -960,7 +1117,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is accessibility focused. */ public boolean isAccessibilityFocused() { - return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED); + return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); } /** @@ -976,7 +1133,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setAccessibilityFocused(boolean focused) { - setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused); + setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); } /** @@ -985,7 +1142,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is selected. */ public boolean isSelected() { - return getBooleanProperty(PROPERTY_SELECTED); + return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED); } /** @@ -1001,7 +1158,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setSelected(boolean selected) { - setBooleanProperty(PROPERTY_SELECTED, selected); + setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected); } /** @@ -1010,7 +1167,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is clickable. */ public boolean isClickable() { - return getBooleanProperty(PROPERTY_CLICKABLE); + return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE); } /** @@ -1026,7 +1183,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setClickable(boolean clickable) { - setBooleanProperty(PROPERTY_CLICKABLE, clickable); + setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable); } /** @@ -1035,7 +1192,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is long clickable. */ public boolean isLongClickable() { - return getBooleanProperty(PROPERTY_LONG_CLICKABLE); + return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE); } /** @@ -1051,7 +1208,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setLongClickable(boolean longClickable) { - setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); + setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable); } /** @@ -1060,7 +1217,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is enabled. */ public boolean isEnabled() { - return getBooleanProperty(PROPERTY_ENABLED); + return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED); } /** @@ -1076,7 +1233,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setEnabled(boolean enabled) { - setBooleanProperty(PROPERTY_ENABLED, enabled); + setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled); } /** @@ -1085,7 +1242,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is a password. */ public boolean isPassword() { - return getBooleanProperty(PROPERTY_PASSWORD); + return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD); } /** @@ -1101,7 +1258,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setPassword(boolean password) { - setBooleanProperty(PROPERTY_PASSWORD, password); + setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password); } /** @@ -1110,7 +1267,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if the node is scrollable, false otherwise. */ public boolean isScrollable() { - return getBooleanProperty(PROPERTY_SCROLLABLE); + return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE); } /** @@ -1127,7 +1284,32 @@ public class AccessibilityNodeInfo implements Parcelable { */ public void setScrollable(boolean scrollable) { enforceNotSealed(); - setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); + setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable); + } + + /** + * Gets if the node is editable. + * + * @return True if the node is editable, false otherwise. + */ + public boolean isEditable() { + return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE); + } + + /** + * Sets whether this node is editable. + * <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 editable True if the node is editable. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setEditable(boolean editable) { + setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable); } /** @@ -1349,6 +1531,75 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Sets the fully qualified resource name of the source view's id. + * + * <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 viewIdResName The id resource name. + */ + public void setViewIdResourceName(CharSequence viewIdResName) { + enforceNotSealed(); + mViewIdResourceName = viewIdResName; + } + + /** + * Gets the fully qualified resource name of the source view's id. + * + * <p> + * <strong>Note:</strong> The primary usage of this API is for UI test automation + * and in order to report the source view id of an {@link AccessibilityNodeInfo} the + * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} + * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. + * </p> + + * @return The id resource name. + */ + public CharSequence getViewIdResourceName() { + return mViewIdResourceName; + } + + /** + * Gets the text selection start. + * + * @return The text selection start if there is selection or -1. + */ + public int getTextSelectionStart() { + return mTextSelectionStart; + } + + /** + * Gets the text selection end. + * + * @return The text selection end if there is selection or -1. + */ + public int getTextSelectionEnd() { + return mTextSelectionEnd; + } + + /** + * Sets the text selection start and end. + * <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 start The text selection start. + * @param end The text selection end. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setTextSelection(int start, int end) { + enforceNotSealed(); + mTextSelectionStart = start; + mTextSelectionEnd = end; + } + + /** * Gets the value of a boolean property. * * @param property The property. @@ -1517,17 +1768,8 @@ public class AccessibilityNodeInfo implements Parcelable { * @return An instance. */ public static AccessibilityNodeInfo obtain() { - synchronized (sPoolLock) { - if (sPool != null) { - AccessibilityNodeInfo info = sPool; - sPool = sPool.mNext; - sPoolSize--; - info.mNext = null; - info.mIsInPool = false; - return info; - } - return new AccessibilityNodeInfo(); - } + AccessibilityNodeInfo info = sPool.acquire(); + return (info != null) ? info : new AccessibilityNodeInfo(); } /** @@ -1552,18 +1794,8 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If the info is already recycled. */ public void recycle() { - if (mIsInPool) { - throw new IllegalStateException("Info already recycled!"); - } clear(); - synchronized (sPoolLock) { - if (sPoolSize <= MAX_POOL_SIZE) { - mNext = sPool; - sPool = this; - mIsInPool = true; - sPoolSize++; - } - } + sPool.release(this); } /** @@ -1609,6 +1841,10 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeCharSequence(mClassName); parcel.writeCharSequence(mText); parcel.writeCharSequence(mContentDescription); + parcel.writeCharSequence(mViewIdResourceName); + + parcel.writeInt(mTextSelectionStart); + parcel.writeInt(mTextSelectionEnd); // Since instances of this class are fetched via synchronous i.e. blocking // calls in IPCs we always recycle as soon as the instance is marshaled. @@ -1620,7 +1856,6 @@ public class AccessibilityNodeInfo implements Parcelable { * * @param other The other instance. */ - @SuppressWarnings("unchecked") private void init(AccessibilityNodeInfo other) { mSealed = other.mSealed; mSourceNodeId = other.mSourceNodeId; @@ -1635,6 +1870,7 @@ public class AccessibilityNodeInfo implements Parcelable { mClassName = other.mClassName; mText = other.mText; mContentDescription = other.mContentDescription; + mViewIdResourceName = other.mViewIdResourceName; mActions= other.mActions; mBooleanProperties = other.mBooleanProperties; mMovementGranularities = other.mMovementGranularities; @@ -1642,6 +1878,8 @@ public class AccessibilityNodeInfo implements Parcelable { for (int i = 0; i < otherChildIdCount; i++) { mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); } + mTextSelectionStart = other.mTextSelectionStart; + mTextSelectionEnd = other.mTextSelectionEnd; } /** @@ -1685,6 +1923,10 @@ public class AccessibilityNodeInfo implements Parcelable { mClassName = parcel.readCharSequence(); mText = parcel.readCharSequence(); mContentDescription = parcel.readCharSequence(); + mViewIdResourceName = parcel.readCharSequence(); + + mTextSelectionStart = parcel.readInt(); + mTextSelectionEnd = parcel.readInt(); } /** @@ -1707,7 +1949,10 @@ public class AccessibilityNodeInfo implements Parcelable { mClassName = null; mText = null; mContentDescription = null; + mViewIdResourceName = null; mActions = 0; + mTextSelectionStart = UNDEFINED; + mTextSelectionEnd = UNDEFINED; } /** @@ -1746,8 +1991,16 @@ public class AccessibilityNodeInfo implements Parcelable { return "ACTION_SCROLL_FORWARD"; case ACTION_SCROLL_BACKWARD: return "ACTION_SCROLL_BACKWARD"; + case ACTION_CUT: + return "ACTION_CUT"; + case ACTION_COPY: + return "ACTION_COPY"; + case ACTION_PASTE: + return "ACTION_PASTE"; + case ACTION_SET_SELECTION: + return "ACTION_SET_SELECTION"; default: - throw new IllegalArgumentException("Unknown action: " + action); + return"ACTION_UNKNOWN"; } } @@ -1851,6 +2104,7 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; className: ").append(mClassName); builder.append("; text: ").append(mText); builder.append("; contentDescription: ").append(mContentDescription); + builder.append("; viewIdResName: ").append(mViewIdResourceName); builder.append("; checkable: ").append(isCheckable()); builder.append("; checked: ").append(isChecked()); diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 9b39300..8d15472 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -17,6 +17,7 @@ package android.view.accessibility; import android.os.Bundle; +import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; @@ -30,23 +31,23 @@ oneway interface IAccessibilityInteractionConnection { void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); - void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void findFocus(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void focusSearch(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 08e30aa..54c2ba5 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -81,6 +81,11 @@ public final class InputMethodInfo implements Parcelable { private boolean mIsAuxIme; /** + * Cavert: mForceDefault must be false for production. This flag is only for test. + */ + private final boolean mForceDefault; + + /** * Constructor. * * @param context The Context in which we are parsing the input method. @@ -108,6 +113,7 @@ public final class InputMethodInfo implements Parcelable { ServiceInfo si = service.serviceInfo; mId = new ComponentName(si.packageName, si.name).flattenToShortString(); mIsAuxIme = true; + mForceDefault = false; PackageManager pm = context.getPackageManager(); String settingsActivityComponent = null; @@ -215,13 +221,39 @@ public final class InputMethodInfo implements Parcelable { mIsAuxIme = source.readInt() == 1; mService = ResolveInfo.CREATOR.createFromParcel(source); source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR); + mForceDefault = false; } /** - * Temporary API for creating a built-in input method. + * Temporary API for creating a built-in input method for test. */ public InputMethodInfo(String packageName, String className, CharSequence label, String settingsActivity) { + this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null, + 0, false); + } + + /** + * Temporary API for creating a built-in input method for test. + * @hide + */ + public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, + String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, + boolean forceDefault) { + final ServiceInfo si = ri.serviceInfo; + mService = ri; + mId = new ComponentName(si.packageName, si.name).flattenToShortString(); + mSettingsActivityName = settingsActivity; + mIsDefaultResId = isDefaultResId; + mIsAuxIme = isAuxIme; + if (subtypes != null) { + mSubtypes.addAll(subtypes); + } + mForceDefault = forceDefault; + } + + private static ResolveInfo buildDummyResolveInfo(String packageName, String className, + CharSequence label) { ResolveInfo ri = new ResolveInfo(); ServiceInfo si = new ServiceInfo(); ApplicationInfo ai = new ApplicationInfo(); @@ -234,11 +266,7 @@ public final class InputMethodInfo implements Parcelable { si.exported = true; si.nonLocalizedLabel = label; ri.serviceInfo = si; - mService = ri; - mId = new ComponentName(si.packageName, si.name).flattenToShortString(); - mSettingsActivityName = settingsActivity; - mIsDefaultResId = 0; - mIsAuxIme = false; + return ri; } /** @@ -340,6 +368,22 @@ public final class InputMethodInfo implements Parcelable { return mIsDefaultResId; } + /** + * Return whether or not this ime is a default ime or not. + * @hide + */ + public boolean isDefault(Context context) { + if (mForceDefault) { + return true; + } + try { + final Resources res = context.createPackageContext(getPackageName(), 0).getResources(); + return res.getBoolean(getIsDefaultResourceId()); + } catch (NameNotFoundException e) { + return false; + } + } + public void dump(Printer pw, String prefix) { pw.println(prefix + "mId=" + mId + " mSettingsActivityName=" + mSettingsActivityName); diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java index 008a615..8008a6b 100644 --- a/core/java/android/webkit/AccessibilityInjector.java +++ b/core/java/android/webkit/AccessibilityInjector.java @@ -318,12 +318,15 @@ class AccessibilityInjector { /** * Attempts to handle selection change events when accessibility is using a * non-JavaScript method. + * <p> + * This must not be called from the main thread. * - * @param selectionString The selection string. + * @param selection The selection string. + * @param token The selection request token. */ - public void handleSelectionChangedIfNecessary(String selectionString) { + public void onSelectionStringChangedWebCoreThread(String selection, int token) { if (mAccessibilityInjectorFallback != null) { - mAccessibilityInjectorFallback.onSelectionStringChange(selectionString); + mAccessibilityInjectorFallback.onSelectionStringChangedWebCoreThread(selection, token); } } diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java index 783b3db..6417527 100644 --- a/core/java/android/webkit/AccessibilityInjectorFallback.java +++ b/core/java/android/webkit/AccessibilityInjectorFallback.java @@ -27,8 +27,9 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.webkit.WebViewCore.EventHub; +import com.android.internal.os.SomeArgs; + import java.util.ArrayList; -import java.util.Stack; /** * This class injects accessibility into WebViews with disabled JavaScript or @@ -48,8 +49,7 @@ import java.util.Stack; * </p> * The possible actions are invocations to * {@link #setCurrentAxis(int, boolean, String)}, or - * {@link #traverseCurrentAxis(int, boolean, String)} - * {@link #traverseGivenAxis(int, int, boolean, String)} + * {@link #traverseGivenAxis(int, int, boolean, String, boolean)} * {@link #performAxisTransition(int, int, boolean, String)} * referred via the values of: * {@link #ACTION_SET_CURRENT_AXIS}, @@ -74,6 +74,9 @@ class AccessibilityInjectorFallback { private static final int ACTION_PERFORM_AXIS_TRANSITION = 3; private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4; + /** Timeout after which asynchronous granular movement is aborted. */ + private static final int MODIFY_SELECTION_TIMEOUT = 500; + // WebView navigation axes from WebViewCore.h, plus an additional axis for // the default behavior. private static final int NAVIGATION_AXIS_CHARACTER = 0; @@ -81,7 +84,8 @@ class AccessibilityInjectorFallback { private static final int NAVIGATION_AXIS_SENTENCE = 2; @SuppressWarnings("unused") private static final int NAVIGATION_AXIS_HEADING = 3; - private static final int NAVIGATION_AXIS_SIBLING = 5; + @SuppressWarnings("unused") + private static final int NAVIGATION_AXIS_SIBLING = 4; @SuppressWarnings("unused") private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5; private static final int NAVIGATION_AXIS_DOCUMENT = 6; @@ -99,8 +103,11 @@ class AccessibilityInjectorFallback { private final WebViewClassic mWebView; private final WebView mWebViewInternal; - // events scheduled for sending as soon as we receive the selected text - private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>(); + // Event scheduled for sending as soon as we receive the selected text. + private AccessibilityEvent mScheduledEvent; + + // Token required to send the scheduled event. + private int mScheduledToken = 0; // the current traversal axis private int mCurrentAxis = 2; // sentence @@ -114,6 +121,15 @@ class AccessibilityInjectorFallback { // keep track of last direction private int mLastDirection; + // Lock used for asynchronous selection callback. + private final Object mCallbackLock = new Object(); + + // Whether the asynchronous selection callback was received. + private boolean mCallbackReceived; + + // Whether the asynchronous selection callback succeeded. + private boolean mCallbackResult; + /** * Creates a new injector associated with a given {@link WebViewClassic}. * @@ -174,8 +190,8 @@ class AccessibilityInjectorFallback { } mLastDirection = direction; sendEvent = (binding.getSecondArgument(i) == 1); - mLastDownEventHandled = traverseCurrentAxis(direction, sendEvent, - contentDescription); + mLastDownEventHandled = traverseGivenAxis( + direction, mCurrentAxis, sendEvent, contentDescription, false); break; case ACTION_TRAVERSE_GIVEN_AXIS: direction = binding.getFirstArgument(i); @@ -187,7 +203,7 @@ class AccessibilityInjectorFallback { mLastDirection = direction; axis = binding.getSecondArgument(i); sendEvent = (binding.getThirdArgument(i) == 1); - traverseGivenAxis(direction, axis, sendEvent, contentDescription); + traverseGivenAxis(direction, axis, sendEvent, contentDescription, false); mLastDownEventHandled = true; break; case ACTION_PERFORM_AXIS_TRANSITION: @@ -207,7 +223,7 @@ class AccessibilityInjectorFallback { mLastDirection = binding.getFirstArgument(i); sendEvent = (binding.getSecondArgument(i) == 1); traverseGivenAxis(mLastDirection, NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR, - sendEvent, contentDescription); + sendEvent, contentDescription, false); mLastDownEventHandled = false; } else { mLastDownEventHandled = true; @@ -222,8 +238,7 @@ class AccessibilityInjectorFallback { } /** - * Set the current navigation axis which will be used while - * calling {@link #traverseCurrentAxis(int, boolean, String)}. + * Set the current navigation axis. * * @param axis The axis to set. * @param sendEvent Whether to send an accessibility event to @@ -255,20 +270,6 @@ class AccessibilityInjectorFallback { } } - /** - * Traverse the document along the current navigation axis. - * - * @param direction The direction of traversal. - * @param sendEvent Whether to send an accessibility event to - * announce the change. - * @param contentDescription A description of the performed action. - * @see #setCurrentAxis(int, boolean, String) - */ - private boolean traverseCurrentAxis(int direction, boolean sendEvent, - String contentDescription) { - return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription); - } - boolean performAccessibilityAction(int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: @@ -276,14 +277,14 @@ class AccessibilityInjectorFallback { final int direction = getDirectionForAction(action); final int axis = getAxisForGranularity(arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT)); - return traverseGivenAxis(direction, axis, true, null); + return traverseGivenAxis(direction, axis, true, null, true); } case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: { final int direction = getDirectionForAction(action); // TODO: Add support for moving by object. final int axis = NAVIGATION_AXIS_SENTENCE; - return traverseGivenAxis(direction, axis, true, null); + return traverseGivenAxis(direction, axis, true, null, true); } default: return false; @@ -293,7 +294,7 @@ class AccessibilityInjectorFallback { /** * Returns the {@link WebView}-defined direction for the given * {@link AccessibilityNodeInfo}-defined action. - * + * * @param action An accessibility action identifier. * @return A web view navigation direction. */ @@ -313,7 +314,7 @@ class AccessibilityInjectorFallback { /** * Returns the {@link WebView}-defined axis for the given * {@link AccessibilityNodeInfo}-defined granularity. - * + * * @param granularity An accessibility granularity identifier. * @return A web view navigation axis. */ @@ -345,20 +346,20 @@ class AccessibilityInjectorFallback { * @param contentDescription A description of the performed action. */ private boolean traverseGivenAxis(int direction, int axis, boolean sendEvent, - String contentDescription) { - WebViewCore webViewCore = mWebView.getWebViewCore(); + String contentDescription, boolean sychronous) { + final WebViewCore webViewCore = mWebView.getWebViewCore(); if (webViewCore == null) { return false; } - AccessibilityEvent event = null; if (sendEvent) { - event = getPartialyPopulatedAccessibilityEvent( + final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent( AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); - // the text will be set upon receiving the selection string + // The text will be set upon receiving the selection string. event.setContentDescription(contentDescription); + mScheduledEvent = event; + mScheduledToken++; } - mScheduledEventStack.push(event); // if the axis is the default let WebView handle the event which will // result in cursor ring movement and selection of its content @@ -366,27 +367,78 @@ class AccessibilityInjectorFallback { return false; } - webViewCore.sendMessage(EventHub.MODIFY_SELECTION, direction, axis); - return true; + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = direction; + args.argi2 = axis; + args.argi3 = mScheduledToken; + + // If we don't need synchronous results, just return true. + if (!sychronous) { + webViewCore.sendMessage(EventHub.MODIFY_SELECTION, args); + return true; + } + + final boolean callbackResult; + + synchronized (mCallbackLock) { + mCallbackReceived = false; + + // Asynchronously changes the selection in WebView, which responds by + // calling onSelectionStringChanged(). + webViewCore.sendMessage(EventHub.MODIFY_SELECTION, args); + + try { + mCallbackLock.wait(MODIFY_SELECTION_TIMEOUT); + } catch (InterruptedException e) { + // Do nothing. + } + + callbackResult = mCallbackResult; + } + + return (mCallbackReceived && callbackResult); } - /** - * Called when the <code>selectionString</code> has changed. - */ - public void onSelectionStringChange(String selectionString) { + /* package */ void onSelectionStringChangedWebCoreThread( + final String selection, final int token) { + synchronized (mCallbackLock) { + mCallbackReceived = true; + mCallbackResult = (selection != null); + mCallbackLock.notifyAll(); + } + + // Managing state and sending events must take place on the UI thread. + mWebViewInternal.post(new Runnable() { + @Override + public void run() { + onSelectionStringChangedMainThread(selection, token); + } + }); + } + + private void onSelectionStringChangedMainThread(String selection, int token) { if (DEBUG) { - Log.d(LOG_TAG, "Selection string: " + selectionString); + Log.d(LOG_TAG, "Selection string: " + selection); } - mIsLastSelectionStringNull = (selectionString == null); - if (mScheduledEventStack.isEmpty()) { + + if (token != mScheduledToken) { + if (DEBUG) { + Log.d(LOG_TAG, "Selection string has incorrect token: " + token); + } return; } - AccessibilityEvent event = mScheduledEventStack.pop(); - if ((event != null) && (selectionString != null)) { - event.getText().add(selectionString); + + mIsLastSelectionStringNull = (selection == null); + + final AccessibilityEvent event = mScheduledEvent; + mScheduledEvent = null; + + if ((event != null) && (selection != null)) { + event.getText().add(selection); event.setFromIndex(0); - event.setToIndex(selectionString.length()); + event.setToIndex(selection.length()); sendAccessibilityEvent(event); + event.recycle(); } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index c3a1a17..0b7e92f 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -226,8 +226,6 @@ class BrowserFrame extends Handler { } else { sJavaBridge.setCacheSize(4 * 1024 * 1024); } - // initialize CacheManager - CacheManager.init(appContext); // create CookieSyncManager with current Context CookieSyncManager.createInstance(appContext); // create PluginManager with current Context diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index 52f41e6..bbd3f2b 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -45,14 +45,6 @@ import java.util.Map; // CacheManager may only be used if your activity contains a WebView. @Deprecated public final class CacheManager { - - private static final String LOGTAG = "cache"; - - static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since"; - static final String HEADER_KEY_IFNONEMATCH = "if-none-match"; - - private static File mBaseDir; - /** * Represents a resource stored in the HTTP cache. Instances of this class * can be obtained by calling @@ -239,39 +231,23 @@ public final class CacheManager { } /** - * Initializes the HTTP cache. This method must be called before any - * CacheManager methods are used. Note that this is called automatically - * when a {@link WebView} is created. - * - * @param context the application context - */ - static void init(Context context) { - // This isn't actually where the real cache lives, but where we put files for the - // purpose of getCacheFile(). - mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging"); - if (!mBaseDir.exists()) { - mBaseDir.mkdirs(); - } - } - - /** * Gets the base directory in which the files used to store the contents of * cache entries are placed. See * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}. * * @return the base directory of the cache - * @deprecated Access to the HTTP cache will be removed in a future release. + * @deprecated This method no longer has any effect and always returns null. */ @Deprecated public static File getCacheFileBaseDir() { - return mBaseDir; + return null; } /** * Gets whether the HTTP cache is disabled. * * @return true if the HTTP cache is disabled - * @deprecated Access to the HTTP cache will be removed in a future release. + * @deprecated This method no longer has any effect and always returns false. */ @Deprecated public static boolean cacheDisabled() { @@ -314,73 +290,11 @@ public final class CacheManager { * @param headers a map from HTTP header name to value, to be populated * for the returned cache entry * @return the cache entry for the specified URL - * @deprecated Access to the HTTP cache will be removed in a future release. + * @deprecated This method no longer has any effect and always returns null. */ @Deprecated public static CacheResult getCacheFile(String url, Map<String, String> headers) { - return getCacheFile(url, 0, headers); - } - - static CacheResult getCacheFile(String url, long postIdentifier, - Map<String, String> headers) { - CacheResult result = nativeGetCacheResult(url); - if (result == null) { - return null; - } - // A temporary local file will have been created native side and localPath set - // appropriately. - File src = new File(mBaseDir, result.localPath); - try { - // Open the file here so that even if it is deleted, the content - // is still readable by the caller until close() is called. - result.inStream = new FileInputStream(src); - } catch (FileNotFoundException e) { - Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e); - // TODO: The files in the cache directory can be removed by the - // system. If it is gone, what should we do? - return null; - } - - // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply - // that we should provide the cache result even if it is expired. - // Note that a negative expires value means a time in the far future. - if (headers != null && result.expires >= 0 - && result.expires <= System.currentTimeMillis()) { - if (result.lastModified == null && result.etag == null) { - return null; - } - // Return HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE - // for requesting validation. - if (result.etag != null) { - headers.put(HEADER_KEY_IFNONEMATCH, result.etag); - } - if (result.lastModified != null) { - headers.put(HEADER_KEY_IFMODIFIEDSINCE, result.lastModified); - } - } - - if (DebugFlags.CACHE_MANAGER) { - Log.v(LOGTAG, "getCacheFile for url " + url); - } - - return result; - } - - /** - * Given a URL and its full headers, gets a CacheResult if a local cache - * can be stored. Otherwise returns null. The mimetype is passed in so that - * the function can use the mimetype that will be passed to WebCore which - * could be different from the mimetype defined in the headers. - * forceCache is for out-of-package callers to force creation of a - * CacheResult, and is used to supply surrogate responses for URL - * interception. - * - * @return a CacheResult for a given URL - */ - static CacheResult createCacheFile(String url, int statusCode, - Headers headers, String mimeType, boolean forceCache) { - // This method is public but hidden. We break functionality. return null; } @@ -424,36 +338,4 @@ public final class CacheManager { // use, we should already have thrown an exception above. assert false; } - - /** - * Removes all cache files. - * - * @return whether the removal succeeded - */ - static boolean removeAllCacheFiles() { - // delete cache files in a separate thread to not block UI. - final Runnable clearCache = new Runnable() { - public void run() { - // delete all cache files - try { - String[] files = mBaseDir.list(); - // if mBaseDir doesn't exist, files can be null. - if (files != null) { - for (int i = 0; i < files.length; i++) { - File f = new File(mBaseDir, files[i]); - if (!f.delete()) { - Log.e(LOGTAG, f.getPath() + " delete failed."); - } - } - } - } catch (SecurityException e) { - // Ignore SecurityExceptions. - } - } - }; - new Thread(clearCache).start(); - return true; - } - - private static native CacheResult nativeGetCacheResult(String url); } diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags index 082a437..b0b5493 100644 --- a/core/java/android/webkit/EventLogTags.logtags +++ b/core/java/android/webkit/EventLogTags.logtags @@ -8,4 +8,3 @@ option java_package android.webkit; # 70103- used by the browser app itself 70150 browser_snap_center -70151 browser_text_size_change (oldSize|1|5), (newSize|1|5) diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java index 6a627e1..c68b450 100644 --- a/core/java/android/webkit/FindActionModeCallback.java +++ b/core/java/android/webkit/FindActionModeCallback.java @@ -33,12 +33,15 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; -class FindActionModeCallback implements ActionMode.Callback, TextWatcher, - View.OnClickListener { +/** + * @hide + */ +public class FindActionModeCallback implements ActionMode.Callback, TextWatcher, + View.OnClickListener, WebView.FindListener { private View mCustomView; private EditText mEditText; private TextView mMatches; - private WebViewClassic mWebView; + private WebView mWebView; private InputMethodManager mInput; private Resources mResources; private boolean mMatchesFound; @@ -46,7 +49,7 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, private int mActiveMatchIndex; private ActionMode mActionMode; - FindActionModeCallback(Context context) { + public FindActionModeCallback(Context context) { mCustomView = LayoutInflater.from(context).inflate( com.android.internal.R.layout.webview_find, null); mEditText = (EditText) mCustomView.findViewById( @@ -61,7 +64,7 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, mResources = context.getResources(); } - void finish() { + public void finish() { mActionMode.finish(); } @@ -69,7 +72,7 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, * Place text in the text field so it can be searched for. Need to press * the find next or find previous button to find all of the matches. */ - void setText(String text) { + public void setText(String text) { mEditText.setText(text); Spannable span = (Spannable) mEditText.getText(); int length = span.length(); @@ -84,15 +87,23 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, } /* - * Set the WebView to search. Must be non null, and set before calling - * startActionMode. + * Set the WebView to search. Must be non null. */ - void setWebView(WebViewClassic webView) { + public void setWebView(WebView webView) { if (null == webView) { throw new AssertionError("WebView supplied to " + "FindActionModeCallback cannot be null"); } mWebView = webView; + mWebView.setFindDialogFindListener(this); + } + + @Override + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + if (isDoneCounting) { + updateMatchCount(activeMatchOrdinal, numberOfMatches, numberOfMatches == 0); + } } /* @@ -121,7 +132,7 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, /* * Highlight all the instances of the string from mEditText in mWebView. */ - void findAll() { + public void findAll() { if (mWebView == null) { throw new AssertionError( "No WebView for FindActionModeCallback::findAll"); @@ -208,7 +219,8 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, public void onDestroyActionMode(ActionMode mode) { mActionMode = null; mWebView.notifyFindDialogDismissed(); - mInput.hideSoftInputFromWindow(mWebView.getWebView().getWindowToken(), 0); + mWebView.setFindDialogFindListener(null); + mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); } @Override @@ -222,7 +234,7 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, throw new AssertionError( "No WebView for FindActionModeCallback::onActionItemClicked"); } - mInput.hideSoftInputFromWindow(mWebView.getWebView().getWindowToken(), 0); + mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); switch(item.getItemId()) { case com.android.internal.R.id.find_prev: findNext(false); diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java index 9c0f754..bc3d035 100644 --- a/core/java/android/webkit/GeolocationPermissions.java +++ b/core/java/android/webkit/GeolocationPermissions.java @@ -61,7 +61,8 @@ public class GeolocationPermissions { }; /** - * Gets the singleton instance of this class. + * Gets the singleton instance of this class. This method cannot be + * called before the application instantiates a {@link WebView} instance. * * @return the singleton {@link GeolocationPermissions} instance */ diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java index 296d960..ee3b369 100644 --- a/core/java/android/webkit/HttpAuthHandler.java +++ b/core/java/android/webkit/HttpAuthHandler.java @@ -40,7 +40,7 @@ public class HttpAuthHandler extends Handler { * previously been rejected by the server for the current request. * * @return whether the credentials are suitable for use - * @see Webview#getHttpAuthUsernamePassword + * @see WebView#getHttpAuthUsernamePassword */ public boolean useHttpAuthUsernamePassword() { return false; diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java index 3a43950..af31544 100644 --- a/core/java/android/webkit/SslErrorHandler.java +++ b/core/java/android/webkit/SslErrorHandler.java @@ -19,9 +19,11 @@ package android.webkit; import android.os.Handler; /** - * SslErrorHandler: class responsible for handling SSL errors. - * This class is passed as a parameter to BrowserCallback.displaySslErrorDialog - * and is meant to receive the user's response. + * Represents a request for handling an SSL error. Instances of this class are + * created by the WebView and passed to + * {@link WebViewClient#onReceivedSslError}. The host application must call + * either {@link #proceed} or {@link #cancel} to set the WebView's response + * to the request. */ public class SslErrorHandler extends Handler { diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java index 096d4cda..1d44b96 100644 --- a/core/java/android/webkit/ViewStateSerializer.java +++ b/core/java/android/webkit/ViewStateSerializer.java @@ -31,7 +31,8 @@ class ViewStateSerializer { private static final int WORKING_STREAM_STORAGE = 16 * 1024; - static final int VERSION = 1; + // VERSION = 1 was for pictures encoded using a previous copy of libskia + static final int VERSION = 2; static boolean serializeViewState(OutputStream stream, DrawData draw) throws IOException { diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index aa68904..728bcd3 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -37,6 +37,10 @@ public abstract class WebSettings { * <li>SINGLE_COLUMN moves all content into one column that is the width of the * view.</li> * <li>NARROW_COLUMNS makes all columns no wider than the screen if possible.</li> + * <li>TEXT_AUTOSIZING boosts font size of paragraphs based on heuristics to make + * the text readable when viewing a wide-viewport layout in the overview mode. + * It is recommended to enable zoom support {@link #setSupportZoom} when + * using this mode.</li> * </ul> */ // XXX: These must match LayoutAlgorithm in Settings.h in WebCore. @@ -47,7 +51,11 @@ public abstract class WebSettings { */ @Deprecated SINGLE_COLUMN, - NARROW_COLUMNS + NARROW_COLUMNS, + /** + * @hide + */ + TEXT_AUTOSIZING } /** @@ -89,6 +97,14 @@ public abstract class WebSettings { ZoomDensity(int size) { value = size; } + + /** + * @hide Only for use by WebViewProvider implementations + */ + public int getValue() { + return value; + } + int value; } @@ -388,16 +404,14 @@ public abstract class WebSettings { } /** - * Sets whether the WebView should save form data. The default is true, - * unless in private browsing mode, when the value is always false. + * Sets whether the WebView should save form data. The default is true. */ public void setSaveFormData(boolean save) { throw new MustOverrideException(); } /** - * Gets whether the WebView saves form data. Always false in private - * browsing mode. + * Gets whether the WebView saves form data. * * @return whether the WebView saves form data * @see #setSaveFormData @@ -580,18 +594,25 @@ public abstract class WebSettings { } /** - * Tells the WebView to use a wide viewport. The default is false. + * Sets whether the WebView should enable support for the "viewport" + * HTML meta tag or should use a wide viewport. + * When the value of the setting is false, the layout width is always set to the + * width of the WebView control in device-independent (CSS) pixels. + * When the value is true and the page contains the viewport meta tag, the value + * of the width specified in the tag is used. If the page does not contain the tag or + * does not provide a width, then a wide viewport will be used. * - * @param use whether to use a wide viewport + * @param use whether to enable support for the viewport meta tag */ public synchronized void setUseWideViewPort(boolean use) { throw new MustOverrideException(); } /** - * Gets whether the WebView is using a wide viewport. + * Gets whether the WebView supports the "viewport" + * HTML meta tag or will use a wide viewport. * - * @return true if the WebView is using a wide viewport + * @return true if the WebView supports the viewport meta tag * @see #setUseWideViewPort */ public synchronized boolean getUseWideViewPort() { @@ -936,6 +957,9 @@ public abstract class WebSettings { * access to content from other file scheme URLs. See * {@link #setAllowFileAccessFromFileURLs}. To enable the most restrictive, * and therefore secure policy, this setting should be disabled. + * Note that this setting affects only JavaScript access to file scheme + * resources. Other access to such resources, for example, from image HTML + * elements, is unaffected. * <p> * The default value is true for API level * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, @@ -953,6 +977,9 @@ public abstract class WebSettings { * enable the most restrictive, and therefore secure policy, this setting * should be disabled. Note that the value of this setting is ignored if * the value of {@link #getAllowUniversalAccessFromFileURLs} is true. + * Note too, that this setting affects only JavaScript access to file scheme + * resources. Other access to such resources, for example, from image HTML + * elements, is unaffected. * <p> * The default value is true for API level * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, @@ -1052,7 +1079,7 @@ public abstract class WebSettings { * * @param appCachePath a String path to the directory containing * Application Caches files. - * @see setAppCacheEnabled + * @see #setAppCacheEnabled */ public synchronized void setAppCachePath(String appCachePath) { throw new MustOverrideException(); @@ -1121,9 +1148,22 @@ public abstract class WebSettings { } /** - * Sets whether Geolocation is enabled. The default is true. See also - * {@link #setGeolocationDatabasePath} for how to correctly set up - * Geolocation. + * Sets whether Geolocation is enabled. The default is true. + * <p> + * Please note that in order for the Geolocation API to be usable + * by a page in the WebView, the following requirements must be met: + * <ul> + * <li>an application must have permission to access the device location, + * see {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}; + * <li>an application must provide an implementation of the + * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback + * to receive notifications that a page is requesting access to location + * via the JavaScript Geolocation API. + * </ul> + * <p> + * As an option, it is possible to store previous locations and web origin + * permissions in a database. See {@link #setGeolocationDatabasePath}. * * @param flag whether Geolocation should be enabled */ @@ -1295,7 +1335,7 @@ public abstract class WebSettings { * and content is re-validated as needed. When navigating back, content is * not revalidated, instead the content is just retrieved from the cache. * This method allows the client to override this behavior by specifying - * one of {@link #LOAD_DEFAULT}, {@link #LOAD_NORMAL}, + * one of {@link #LOAD_DEFAULT}, * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. * diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java index 1bbe7bb..c10a429 100644 --- a/core/java/android/webkit/WebSettingsClassic.java +++ b/core/java/android/webkit/WebSettingsClassic.java @@ -647,10 +647,6 @@ public class WebSettingsClassic extends WebSettings { @Override public synchronized void setTextZoom(int textZoom) { if (mTextSize != textZoom) { - if (WebViewClassic.mLogEvent) { - EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE, - mTextSize, textZoom); - } mTextSize = textZoom; postSync(); } @@ -820,6 +816,10 @@ public class WebSettingsClassic extends WebSettings { */ @Override public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) { + if (l == LayoutAlgorithm.TEXT_AUTOSIZING) { + throw new IllegalArgumentException( + "WebViewClassic does not support TEXT_AUTOSIZING layout mode"); + } // XXX: This will only be affective if libwebcore was built with // ANDROID_LAYOUT defined. if (mLayoutAlgorithm != l) { diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6df7820..d0bfbe8 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -615,7 +615,7 @@ public class WebView extends AbsoluteLayout * @param realm the realm to which the credentials apply * @param username the username * @param password the password - * @see getHttpAuthUsernamePassword + * @see #getHttpAuthUsernamePassword * @see WebViewDatabase#hasHttpAuthUsernamePassword * @see WebViewDatabase#clearHttpAuthUsernamePassword */ @@ -635,7 +635,7 @@ public class WebView extends AbsoluteLayout * @return the credentials as a String array, if found. The first element * is the username and the second element is the password. Null if * no credentials are found. - * @see setHttpAuthUsernamePassword + * @see #setHttpAuthUsernamePassword * @see WebViewDatabase#hasHttpAuthUsernamePassword * @see WebViewDatabase#clearHttpAuthUsernamePassword */ @@ -1329,7 +1329,8 @@ public class WebView extends AbsoluteLayout */ public void setFindListener(FindListener listener) { checkThread(); - mProvider.setFindListener(listener); + setupFindListenerIfNeeded(); + mFindListener.mUserFindListener = listener; } /** @@ -1850,11 +1851,60 @@ public class WebView extends AbsoluteLayout } //------------------------------------------------------------------------- + // Package-private internal stuff + //------------------------------------------------------------------------- + + // Only used by android.webkit.FindActionModeCallback. + void setFindDialogFindListener(FindListener listener) { + checkThread(); + setupFindListenerIfNeeded(); + mFindListener.mFindDialogFindListener = listener; + } + + // Only used by android.webkit.FindActionModeCallback. + void notifyFindDialogDismissed() { + checkThread(); + mProvider.notifyFindDialogDismissed(); + } + + //------------------------------------------------------------------------- // Private internal stuff //------------------------------------------------------------------------- private WebViewProvider mProvider; + /** + * In addition to the FindListener that the user may set via the WebView.setFindListener + * API, FindActionModeCallback will register it's own FindListener. We keep them separate + * via this class so that that the two FindListeners can potentially exist at once. + */ + private class FindListenerDistributor implements FindListener { + private FindListener mFindDialogFindListener; + private FindListener mUserFindListener; + + @Override + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + if (mFindDialogFindListener != null) { + mFindDialogFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, + isDoneCounting); + } + + if (mUserFindListener != null) { + mUserFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, + isDoneCounting); + } + } + } + private FindListenerDistributor mFindListener; + + private void setupFindListenerIfNeeded() { + if (mFindListener == null) { + mFindListener = new FindListenerDistributor(); + mProvider.setFindListener(mFindListener); + } + } + private void ensureProviderCreated() { checkThread(); if (mProvider == null) { @@ -1865,9 +1915,6 @@ public class WebView extends AbsoluteLayout } private static synchronized WebViewFactoryProvider getFactory() { - // For now the main purpose of this function (and the factory abstration) is to keep - // us honest and minimize usage of WebViewClassic internals when binding the proxy. - checkThread(); return WebViewFactory.getProvider(); } @@ -1910,9 +1957,8 @@ public class WebView extends AbsoluteLayout @Override public void setOverScrollMode(int mode) { super.setOverScrollMode(mode); - // This method may called in the constructor chain, before the WebView provider is - // created. (Fortunately, this is the only method we override that can get called by - // any of the base class constructors). + // This method may be called in the constructor chain, before the WebView provider is + // created. ensureProviderCreated(); mProvider.getViewDelegate().setOverScrollMode(mode); } @@ -2072,6 +2118,9 @@ public class WebView extends AbsoluteLayout @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); + // This method may be called in the constructor chain, before the WebView provider is + // created. + ensureProviderCreated(); mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility); } @@ -2137,4 +2186,10 @@ public class WebView extends AbsoluteLayout super.setLayerType(layerType, paint); mProvider.getViewDelegate().setLayerType(layerType, paint); } + + @Override + protected void dispatchDraw(Canvas canvas) { + mProvider.getViewDelegate().preDispatchDraw(canvas); + super.dispatchDraw(canvas); + } } diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 7154f95..3ded1cd 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -1024,30 +1024,26 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc static final int UPDATE_MATCH_COUNT = 126; static final int CENTER_FIT_RECT = 127; static final int SET_SCROLLBAR_MODES = 129; - static final int SELECTION_STRING_CHANGED = 130; - static final int HIT_TEST_RESULT = 131; - static final int SAVE_WEBARCHIVE_FINISHED = 132; - - static final int SET_AUTOFILLABLE = 133; - static final int AUTOFILL_COMPLETE = 134; - - static final int SCREEN_ON = 136; - static final int UPDATE_ZOOM_DENSITY = 139; - static final int EXIT_FULLSCREEN_VIDEO = 140; - - static final int COPY_TO_CLIPBOARD = 141; - static final int INIT_EDIT_FIELD = 142; - static final int REPLACE_TEXT = 143; - static final int CLEAR_CARET_HANDLE = 144; - static final int KEY_PRESS = 145; - static final int RELOCATE_AUTO_COMPLETE_POPUP = 146; - static final int FOCUS_NODE_CHANGED = 147; - static final int AUTOFILL_FORM = 148; - static final int SCROLL_EDIT_TEXT = 149; - static final int EDIT_TEXT_SIZE_CHANGED = 150; - static final int SHOW_CARET_HANDLE = 151; - static final int UPDATE_CONTENT_BOUNDS = 152; - static final int SCROLL_HANDLE_INTO_VIEW = 153; + static final int HIT_TEST_RESULT = 130; + static final int SAVE_WEBARCHIVE_FINISHED = 131; + static final int SET_AUTOFILLABLE = 132; + static final int AUTOFILL_COMPLETE = 133; + static final int SCREEN_ON = 134; + static final int UPDATE_ZOOM_DENSITY = 135; + static final int EXIT_FULLSCREEN_VIDEO = 136; + static final int COPY_TO_CLIPBOARD = 137; + static final int INIT_EDIT_FIELD = 138; + static final int REPLACE_TEXT = 139; + static final int CLEAR_CARET_HANDLE = 140; + static final int KEY_PRESS = 141; + static final int RELOCATE_AUTO_COMPLETE_POPUP = 142; + static final int FOCUS_NODE_CHANGED = 143; + static final int AUTOFILL_FORM = 144; + static final int SCROLL_EDIT_TEXT = 145; + static final int EDIT_TEXT_SIZE_CHANGED = 146; + static final int SHOW_CARET_HANDLE = 147; + static final int UPDATE_CONTENT_BOUNDS = 148; + static final int SCROLL_HANDLE_INTO_VIEW = 149; private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT; @@ -1800,6 +1796,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0)); } + /* package */ void handleSelectionChangedWebCoreThread(String selection, int token) { + if (isAccessibilityInjectionEnabled()) { + getAccessibilityInjector().onSelectionStringChangedWebCoreThread(selection, token); + } + } + private boolean isAccessibilityInjectionEnabled() { final AccessibilityManager manager = AccessibilityManager.getInstance(mContext); if (!manager.isEnabled()) { @@ -3702,7 +3704,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mCachedOverlappingActionModeHeight = -1; mFindCallback = callback; setFindIsUp(true); - mFindCallback.setWebView(this); + mFindCallback.setWebView(getWebView()); if (showIme) { mFindCallback.showSoftInput(); } else if (text != null) { @@ -3804,7 +3806,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc /** * Called when the find ActionMode ends. */ - void notifyFindDialogDismissed() { + @Override + public void notifyFindDialogDismissed() { mFindCallback = null; mCachedOverlappingActionModeHeight = -1; if (mWebViewCore == null) { @@ -7497,13 +7500,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mVerticalScrollBarMode = msg.arg2; break; - case SELECTION_STRING_CHANGED: - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector() - .handleSelectionChangedIfNecessary((String) msg.obj); - } - break; - case FOCUS_NODE_CHANGED: mIsEditingText = (msg.arg1 == mFieldPointer); if (mAutoCompletePopup != null && !mIsEditingText) { @@ -8562,6 +8558,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc updateHwAccelerated(); } + @Override + public void preDispatchDraw(Canvas canvas) { + // no-op for WebViewClassic. + } + private void updateHwAccelerated() { if (mNativeClass == 0) { return; diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 08a046a..e8974c6 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -31,6 +31,7 @@ public class WebViewClient { * proper handler for the url. If WebViewClient is provided, return true * means the host application handles the url, while return false means the * current WebView handles the url. + * This method is not called for requests using the POST "method". * * @param view The WebView that is initiating the callback. * @param url The url to be loaded. @@ -82,9 +83,9 @@ public class WebViewClient { * Notify the host application of a resource request and allow the * application to return the data. If the return value is null, the WebView * will continue to load the resource as usual. Otherwise, the return - * response and data will be used. NOTE: This method is called by the - * network thread so clients should exercise caution when accessing private - * data. + * response and data will be used. NOTE: This method is called on a thread + * other than the UI thread so clients should exercise caution + * when accessing private data or the view system. * * @param view The {@link android.webkit.WebView} that is requesting the * resource. @@ -213,7 +214,7 @@ public class WebViewClient { * @param handler the HttpAuthHandler used to set the WebView's response * @param host the host requiring authentication * @param realm the realm for which authentication is required - * @see Webview#getHttpAuthUsernamePassword + * @see WebView#getHttpAuthUsernamePassword */ public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index c35b768..4a09636 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -41,6 +41,8 @@ import android.view.View; import android.webkit.WebViewClassic.FocusNodeHref; import android.webkit.WebViewInputDispatcher.WebKitCallbacks; +import com.android.internal.os.SomeArgs; + import junit.framework.Assert; import java.io.OutputStream; @@ -1545,12 +1547,14 @@ public final class WebViewCore { case MODIFY_SELECTION: mTextSelectionChangeReason = TextSelectionData.REASON_ACCESSIBILITY_INJECTOR; - String modifiedSelectionString = - nativeModifySelection(mNativeClass, msg.arg1, - msg.arg2); - mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.SELECTION_STRING_CHANGED, - modifiedSelectionString).sendToTarget(); + final SomeArgs args = (SomeArgs) msg.obj; + final String modifiedSelectionString = nativeModifySelection( + mNativeClass, args.argi1, args.argi2); + // If accessibility is on, the main thread may be + // waiting for a response. Send on webcore thread. + mWebViewClassic.handleSelectionChangedWebCoreThread( + modifiedSelectionString, args.argi3); + args.recycle(); mTextSelectionChangeReason = TextSelectionData.REASON_UNKNOWN; break; @@ -2001,9 +2005,6 @@ public final class WebViewCore { private void clearCache(boolean includeDiskFiles) { mBrowserFrame.clearCache(); - if (includeDiskFiles) { - CacheManager.removeAllCacheFiles(); - } } private void loadUrl(String url, Map<String, String> extraHeaders) { diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 5597259..e08052a 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -50,7 +50,7 @@ public class WebViewDatabase { * * @return true if there are any saved username/password pairs * @see WebView#savePassword - * @see clearUsernamePassword + * @see #clearUsernamePassword */ public boolean hasUsernamePassword() { throw new MustOverrideException(); @@ -61,7 +61,7 @@ public class WebViewDatabase { * Note that these are unrelated to HTTP authentication credentials. * * @see WebView#savePassword - * @see hasUsernamePassword + * @see #hasUsernamePassword */ public void clearUsernamePassword() { throw new MustOverrideException(); @@ -71,9 +71,9 @@ public class WebViewDatabase { * Gets whether there are any saved credentials for HTTP authentication. * * @return whether there are any saved credentials - * @see Webview#getHttpAuthUsernamePassword - * @see Webview#setHttpAuthUsernamePassword - * @see clearHttpAuthUsernamePassword + * @see WebView#getHttpAuthUsernamePassword + * @see WebView#setHttpAuthUsernamePassword + * @see #clearHttpAuthUsernamePassword */ public boolean hasHttpAuthUsernamePassword() { throw new MustOverrideException(); @@ -82,9 +82,9 @@ public class WebViewDatabase { /** * Clears any saved credentials for HTTP authentication. * - * @see Webview#getHttpAuthUsernamePassword - * @see Webview#setHttpAuthUsernamePassword - * @see hasHttpAuthUsernamePassword + * @see WebView#getHttpAuthUsernamePassword + * @see WebView#setHttpAuthUsernamePassword + * @see #hasHttpAuthUsernamePassword */ public void clearHttpAuthUsernamePassword() { throw new MustOverrideException(); @@ -94,7 +94,7 @@ public class WebViewDatabase { * Gets whether there is any saved data for web forms. * * @return whether there is any saved data for web forms - * @see clearFormData + * @see #clearFormData */ public boolean hasFormData() { throw new MustOverrideException(); @@ -103,7 +103,7 @@ public class WebViewDatabase { /** * Clears any saved data for web forms. * - * @see hasFormData + * @see #hasFormData */ public void clearFormData() { throw new MustOverrideException(); diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index b833a01..18df0b1 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -31,7 +31,7 @@ class WebViewFactory { // TODO: When the Chromium powered WebView is ready, it should be the default factory class. private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory"; private static final String CHROMIUM_WEBVIEW_FACTORY = - "com.android.webviewchromium.WebViewChromiumFactoryProvider"; + "com.android.webview.chromium.WebViewChromiumFactoryProvider"; private static final String CHROMIUM_WEBVIEW_JAR = "/system/framework/webviewchromium.jar"; private static final String LOGTAG = "WebViewFactory"; diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index c9f9fbd..fa17ab9 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -240,7 +240,7 @@ public interface WebViewProvider { public View findHierarchyView(String className, int hashCode); //------------------------------------------------------------------------- - // Provider glue methods + // Provider internal methods //------------------------------------------------------------------------- /** @@ -255,6 +255,12 @@ public interface WebViewProvider { */ /* package */ ScrollDelegate getScrollDelegate(); + /** + * Only used by FindActionModeCallback to inform providers that the find dialog has + * been dismissed. + */ + public void notifyFindDialogDismissed(); + //------------------------------------------------------------------------- // View / ViewGroup delegation methods //------------------------------------------------------------------------- @@ -341,6 +347,8 @@ public interface WebViewProvider { public void setBackgroundColor(int color); public void setLayerType(int layerType, Paint paint); + + public void preDispatchDraw(Canvas canvas); } interface ScrollDelegate { diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 57bf0d3..396fd68 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1374,9 +1374,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (isEnabled()) { if (getFirstVisiblePosition() > 0) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + info.setScrollable(true); } if (getLastVisiblePosition() < getCount() - 1) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + info.setScrollable(true); } } } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 3b5e75b..7674837 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -305,7 +305,7 @@ public abstract class AbsSeekBar extends ProgressBar { } // Canvas will be translated, so 0,0 is where we start drawing - final int left = isLayoutRtl() ? available - thumbPos : thumbPos; + final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos; thumb.setBounds(left, topBound, left + thumbWidth, bottomBound); } @@ -426,7 +426,7 @@ public abstract class AbsSeekBar extends ProgressBar { int x = (int)event.getX(); float scale; float progress = 0; - if (isLayoutRtl()) { + if (isLayoutRtl() && mMirrorForRtl) { if (x > width - mPaddingRight) { scale = 0.0f; } else if (x < mPaddingLeft) { diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java index f279f8e..a379157 100644 --- a/core/java/android/widget/AbsSpinner.java +++ b/core/java/android/widget/AbsSpinner.java @@ -375,7 +375,7 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { /** * Constructor called from {@link #CREATOR} */ - private SavedState(Parcel in) { + SavedState(Parcel in) { super(in); selectedId = in.readLong(); position = in.readInt(); diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index 44f04db..97926a7 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -97,11 +97,11 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * Constructor * * @param context The current context. - * @param textViewResourceId The resource ID for a layout file containing a TextView to use when + * @param resource The resource ID for a layout file containing a TextView to use when * instantiating views. */ - public ArrayAdapter(Context context, int textViewResourceId) { - init(context, textViewResourceId, 0, new ArrayList<T>()); + public ArrayAdapter(Context context, int resource) { + init(context, resource, 0, new ArrayList<T>()); } /** @@ -120,12 +120,12 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * Constructor * * @param context The current context. - * @param textViewResourceId The resource ID for a layout file containing a TextView to use when + * @param resource The resource ID for a layout file containing a TextView to use when * instantiating views. * @param objects The objects to represent in the ListView. */ - public ArrayAdapter(Context context, int textViewResourceId, T[] objects) { - init(context, textViewResourceId, 0, Arrays.asList(objects)); + public ArrayAdapter(Context context, int resource, T[] objects) { + init(context, resource, 0, Arrays.asList(objects)); } /** @@ -145,12 +145,12 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * Constructor * * @param context The current context. - * @param textViewResourceId The resource ID for a layout file containing a TextView to use when + * @param resource The resource ID for a layout file containing a TextView to use when * instantiating views. * @param objects The objects to represent in the ListView. */ - public ArrayAdapter(Context context, int textViewResourceId, List<T> objects) { - init(context, textViewResourceId, 0, objects); + public ArrayAdapter(Context context, int resource, List<T> objects) { + init(context, resource, 0, objects); } /** diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java index f1804f8..41ab5f2 100644 --- a/core/java/android/widget/CheckBox.java +++ b/core/java/android/widget/CheckBox.java @@ -20,6 +20,7 @@ import android.content.Context; import android.util.AttributeSet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.util.ValueModel; /** @@ -55,7 +56,9 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#View View Attributes} * </p> */ -public class CheckBox extends CompoundButton { +public class CheckBox extends CompoundButton implements ValueEditor<Boolean> { + private ValueModel<Boolean> mValueModel = ValueModel.EMPTY; + public CheckBox(Context context) { this(context, null); } @@ -79,4 +82,22 @@ public class CheckBox extends CompoundButton { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CheckBox.class.getName()); } + + @Override + public ValueModel<Boolean> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<Boolean> valueModel) { + mValueModel = valueModel; + setChecked(mValueModel.get()); + } + + @Override + public boolean performClick() { + boolean handled = super.performClick(); + mValueModel.set(isChecked()); + return handled; + } } diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 57e51c2..ec81214 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.graphics.Rect; import android.text.Editable; import android.text.Selection; import android.text.Spannable; @@ -24,6 +25,7 @@ import android.text.TextUtils; import android.text.method.ArrowKeyMovementMethod; import android.text.method.MovementMethod; import android.util.AttributeSet; +import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -47,7 +49,9 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#TextView TextView Attributes}, * {@link android.R.styleable#View View Attributes} */ -public class EditText extends TextView { +public class EditText extends TextView implements ValueEditor<CharSequence> { + private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY; + public EditText(Context context) { this(context, null); } @@ -128,4 +132,21 @@ public class EditText extends TextView { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(EditText.class.getName()); } + + @Override + public ValueModel<CharSequence> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<CharSequence> valueModel) { + mValueModel = valueModel; + setText(mValueModel.get()); + } + + @Override + void sendAfterTextChanged(Editable text) { + super.sendAfterTextChanged(text); + mValueModel.set(text); + } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 8892316..dc305a5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -20,6 +20,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.widget.EditableInputConnection; import android.R; +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; import android.content.ClipData; import android.content.ClipData.Item; import android.content.Context; @@ -124,7 +126,6 @@ public class Editor { InputMethodState mInputMethodState; DisplayList[] mTextDisplayLists; - int mLastLayoutHeight; boolean mFrozenWithFocus; boolean mSelectionMoved; @@ -1289,20 +1290,11 @@ public class Editor { mTextDisplayLists = new DisplayList[ArrayUtils.idealObjectArraySize(0)]; } - // If the height of the layout changes (usually when inserting or deleting a line, - // but could be changes within a span), invalidate everything. We could optimize - // more aggressively (for example, adding offsets to blocks) but it would be more - // complex and we would only get the benefit in some cases. - int layoutHeight = layout.getHeight(); - if (mLastLayoutHeight != layoutHeight) { - invalidateTextDisplayList(); - mLastLayoutHeight = layoutHeight; - } - DynamicLayout dynamicLayout = (DynamicLayout) layout; int[] blockEndLines = dynamicLayout.getBlockEndLines(); int[] blockIndices = dynamicLayout.getBlockIndices(); final int numberOfBlocks = dynamicLayout.getNumberOfBlocks(); + final int indexFirstChangedBlock = dynamicLayout.getIndexFirstChangedBlock(); int endOfPreviousBlock = -1; int searchStartIndex = 0; @@ -1324,10 +1316,11 @@ public class Editor { blockDisplayList = mTextDisplayLists[blockIndex] = mTextView.getHardwareRenderer().createDisplayList("Text " + blockIndex); } else { - if (blockIsInvalid) blockDisplayList.invalidate(); + if (blockIsInvalid) blockDisplayList.clear(); } - if (!blockDisplayList.isValid()) { + final boolean blockDisplayListIsInvalid = !blockDisplayList.isValid(); + if (i >= indexFirstChangedBlock || blockDisplayListIsInvalid) { final int blockBeginLine = endOfPreviousBlock + 1; final int top = layout.getLineTop(blockBeginLine); final int bottom = layout.getLineBottom(blockEndLine); @@ -1344,24 +1337,27 @@ public class Editor { right = (int) (max + 0.5f); } - final HardwareCanvas hardwareCanvas = blockDisplayList.start(); - try { - // Tighten the bounds of the viewport to the actual text size - hardwareCanvas.setViewport(right - left, bottom - top); - // The dirty rect should always be null for a display list - hardwareCanvas.onPreDraw(null); - // drawText is always relative to TextView's origin, this translation brings - // this range of text back to the top left corner of the viewport - hardwareCanvas.translate(-left, -top); - layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine); - // No need to untranslate, previous context is popped after drawDisplayList - } finally { - hardwareCanvas.onPostDraw(); - blockDisplayList.end(); - blockDisplayList.setLeftTopRightBottom(left, top, right, bottom); - // Same as drawDisplayList below, handled by our TextView's parent - blockDisplayList.setClipChildren(false); + // Rebuild display list if it is invalid + if (blockDisplayListIsInvalid) { + final HardwareCanvas hardwareCanvas = blockDisplayList.start( + right - left, bottom - top); + try { + // drawText is always relative to TextView's origin, this translation + // brings this range of text back to the top left corner of the viewport + hardwareCanvas.translate(-left, -top); + layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine); + // No need to untranslate, previous context is popped after + // drawDisplayList + } finally { + blockDisplayList.end(); + // Same as drawDisplayList below, handled by our TextView's parent + blockDisplayList.setClipChildren(false); + } } + + // Valid disply list whose index is >= indexFirstChangedBlock + // only needs to update its drawing location. + blockDisplayList.setLeftTopRightBottom(left, top, right, bottom); } ((HardwareCanvas) canvas).drawDisplayList(blockDisplayList, null, @@ -1369,6 +1365,8 @@ public class Editor { endOfPreviousBlock = blockEndLine; } + + dynamicLayout.setIndexFirstChangedBlock(numberOfBlocks); } else { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); @@ -1431,7 +1429,7 @@ public class Editor { while (i < numberOfBlocks) { final int blockIndex = blockIndices[i]; if (blockIndex != DynamicLayout.INVALID_BLOCK_INDEX) { - mTextDisplayLists[blockIndex].invalidate(); + mTextDisplayLists[blockIndex].clear(); } if (blockEndLines[i] >= lastLine) break; i++; @@ -1442,7 +1440,7 @@ public class Editor { void invalidateTextDisplayList() { if (mTextDisplayLists != null) { for (int i = 0; i < mTextDisplayLists.length; i++) { - if (mTextDisplayLists[i] != null) mTextDisplayLists[i].invalidate(); + if (mTextDisplayLists[i] != null) mTextDisplayLists[i].clear(); } } } @@ -1468,20 +1466,24 @@ public class Editor { middle = (top + bottom) >> 1; } - updateCursorPosition(0, top, middle, getPrimaryHorizontal(layout, hintLayout, offset)); + boolean clamped = layout.shouldClampCursor(line); + updateCursorPosition(0, top, middle, + getPrimaryHorizontal(layout, hintLayout, offset, clamped)); if (mCursorCount == 2) { - updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset)); + updateCursorPosition(1, middle, bottom, + layout.getSecondaryHorizontal(offset, clamped)); } } - private float getPrimaryHorizontal(Layout layout, Layout hintLayout, int offset) { + private float getPrimaryHorizontal(Layout layout, Layout hintLayout, int offset, + boolean clamped) { if (TextUtils.isEmpty(layout.getText()) && hintLayout != null && !TextUtils.isEmpty(hintLayout.getText())) { - return hintLayout.getPrimaryHorizontal(offset); + return hintLayout.getPrimaryHorizontal(offset, clamped); } else { - return layout.getPrimaryHorizontal(offset); + return layout.getPrimaryHorizontal(offset, clamped); } } @@ -1890,10 +1892,23 @@ public class Editor { // Make sure there is only at most one EasyEditSpan in the text if (mPopupWindow.mEasyEditSpan != null) { - text.removeSpan(mPopupWindow.mEasyEditSpan); + mPopupWindow.mEasyEditSpan.setDeleteEnabled(false); } mPopupWindow.setEasyEditSpan((EasyEditSpan) span); + mPopupWindow.setOnDeleteListener(new EasyEditDeleteListener() { + @Override + public void onDeleteClick(EasyEditSpan span) { + Editable editable = (Editable) mTextView.getText(); + int start = editable.getSpanStart(span); + int end = editable.getSpanEnd(span); + if (start >= 0 && end >= 0) { + sendNotification(EasyEditSpan.TEXT_DELETED, span); + mTextView.deleteText_internal(start, end); + } + editable.removeSpan(span); + } + }); if (mTextView.getWindowVisibility() != View.VISIBLE) { // The window is not visible yet, ignore the text change. @@ -1927,8 +1942,10 @@ public class Editor { @Override public void onSpanChanged(Spannable text, Object span, int previousStart, int previousEnd, int newStart, int newEnd) { - if (mPopupWindow != null && span == mPopupWindow.mEasyEditSpan) { - text.removeSpan(mPopupWindow.mEasyEditSpan); + if (mPopupWindow != null && span instanceof EasyEditSpan) { + EasyEditSpan easyEditSpan = (EasyEditSpan) span; + sendNotification(EasyEditSpan.TEXT_MODIFIED, easyEditSpan); + text.removeSpan(easyEditSpan); } } @@ -1938,6 +1955,31 @@ public class Editor { mTextView.removeCallbacks(mHidePopup); } } + + private void sendNotification(int textChangedType, EasyEditSpan span) { + try { + PendingIntent pendingIntent = span.getPendingIntent(); + if (pendingIntent != null) { + Intent intent = new Intent(); + intent.putExtra(EasyEditSpan.EXTRA_TEXT_CHANGED_TYPE, textChangedType); + pendingIntent.send(mTextView.getContext(), 0, intent); + } + } catch (CanceledException e) { + // This should not happen, as we should try to send the intent only once. + Log.w(TAG, "PendingIntent for notification cannot be sent", e); + } + } + } + + /** + * Listens for the delete event triggered by {@link EasyEditPopupWindow}. + */ + private interface EasyEditDeleteListener { + + /** + * Clicks the delete pop-up. + */ + void onDeleteClick(EasyEditSpan span); } /** @@ -1950,6 +1992,7 @@ public class Editor { com.android.internal.R.layout.text_edit_action_popup_text; private TextView mDeleteTextView; private EasyEditSpan mEasyEditSpan; + private EasyEditDeleteListener mOnDeleteListener; @Override protected void createPopupWindow() { @@ -1984,16 +2027,26 @@ public class Editor { mEasyEditSpan = easyEditSpan; } + private void setOnDeleteListener(EasyEditDeleteListener listener) { + mOnDeleteListener = listener; + } + @Override public void onClick(View view) { - if (view == mDeleteTextView) { - Editable editable = (Editable) mTextView.getText(); - int start = editable.getSpanStart(mEasyEditSpan); - int end = editable.getSpanEnd(mEasyEditSpan); - if (start >= 0 && end >= 0) { - mTextView.deleteText_internal(start, end); - } + if (view == mDeleteTextView + && mEasyEditSpan != null && mEasyEditSpan.isDeleteEnabled() + && mOnDeleteListener != null) { + mOnDeleteListener.onDeleteClick(mEasyEditSpan); + } + } + + @Override + public void hide() { + if (mEasyEditSpan != null) { + mEasyEditSpan.setDeleteEnabled(false); } + mOnDeleteListener = null; + super.hide(); } @Override @@ -2647,15 +2700,10 @@ public class Editor { suggestionStart, suggestionEnd).toString(); mTextView.replaceText_internal(spanStart, spanEnd, suggestion); - // Notify source IME of the suggestion pick. Do this before swaping texts. - if (!TextUtils.isEmpty( - suggestionInfo.suggestionSpan.getNotificationTargetClassName())) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.notifySuggestionPicked(suggestionInfo.suggestionSpan, originalText, - suggestionInfo.suggestionIndex); - } - } + // Notify source IME of the suggestion pick. Do this before + // swaping texts. + suggestionInfo.suggestionSpan.notifySelection( + mTextView.getContext(), originalText, suggestionInfo.suggestionIndex); // Swap text content between actual text and Suggestion span String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index e0c5bbd..c4ef11c 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -891,7 +891,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList lp = (Gallery.LayoutParams) generateDefaultLayoutParams(); } - addViewInLayout(child, fromLeft != mIsRtl ? -1 : 0, lp); + addViewInLayout(child, fromLeft != mIsRtl ? -1 : 0, lp, true); child.setSelected(offset == 0); diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 772d748..85ed8db 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -605,7 +605,7 @@ public class GridLayout extends ViewGroup { } private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) { - return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, horizontal, leading); + return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading); } private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) { @@ -824,13 +824,11 @@ public class GridLayout extends ViewGroup { // Draw grid private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) { - int dx = getPaddingLeft(); - int dy = getPaddingTop(); if (isLayoutRtl()) { int width = getWidth(); - graphics.drawLine(width - dx - x1, dy + y1, width - dx - x2, dy + y2, paint); + graphics.drawLine(width - x1, y1, width - x2, y2, paint); } else { - graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint); + graphics.drawLine(x1, y1, x2, y2, paint); } } @@ -838,18 +836,17 @@ public class GridLayout extends ViewGroup { * @hide */ @Override - protected void onDebugDrawMargins(Canvas canvas) { + protected void onDebugDrawMargins(Canvas canvas, Paint paint) { // Apply defaults, so as to remove UNDEFINED values LayoutParams lp = new LayoutParams(); for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); - Insets insets = getLayoutMode() == OPTICAL_BOUNDS ? c.getOpticalInsets() : Insets.NONE; lp.setMargins( - getMargin1(c, true, true) - insets.left, - getMargin1(c, false, true) - insets.top, - getMargin1(c, true, false) - insets.right, - getMargin1(c, false, false) - insets.bottom); - lp.onDebugDraw(c, canvas); + getMargin1(c, true, true), + getMargin1(c, false, true), + getMargin1(c, true, false), + getMargin1(c, false, false)); + lp.onDebugDraw(c, canvas, paint); } } @@ -858,26 +855,30 @@ public class GridLayout extends ViewGroup { */ @Override protected void onDebugDraw(Canvas canvas) { - int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int width = getWidth() - getPaddingLeft() - getPaddingRight(); - Paint paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setColor(Color.argb(50, 255, 255, 255)); + Insets insets = getOpticalInsets(); + + int top = getPaddingTop() + insets.top; + int left = getPaddingLeft() + insets.left; + int right = getWidth() - getPaddingRight() - insets.right; + int bottom = getHeight() - getPaddingBottom() - insets.bottom; + int[] xs = horizontalAxis.locations; if (xs != null) { for (int i = 0, length = xs.length; i < length; i++) { - int x = xs[i]; - drawLine(canvas, x, 0, x, height - 1, paint); + int x = left + xs[i]; + drawLine(canvas, x, top, x, bottom, paint); } } int[] ys = verticalAxis.locations; if (ys != null) { for (int i = 0, length = ys.length; i < length; i++) { - int y = ys[i]; - drawLine(canvas, 0, y, width - 1, y, paint); + int y = top + ys[i]; + drawLine(canvas, left, y, right, y, paint); } } @@ -1013,12 +1014,7 @@ public class GridLayout extends ViewGroup { } private int getMeasurement(View c, boolean horizontal) { - int result = horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); - if (getLayoutMode() == OPTICAL_BOUNDS) { - Insets insets = c.getOpticalInsets(); - return result - (horizontal ? insets.left + insets.right : insets.top + insets.bottom); - } - return result; + return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); } final int getMeasurementIncludingMargin(View c, boolean horizontal) { @@ -1124,14 +1120,6 @@ public class GridLayout extends ViewGroup { targetWidth - width - paddingRight - rightMargin - dx; int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin; - boolean useLayoutBounds = getLayoutMode() == OPTICAL_BOUNDS; - if (useLayoutBounds) { - Insets insets = c.getOpticalInsets(); - cx -= insets.left; - cy -= insets.top; - width += (insets.left + insets.right); - height += (insets.top + insets.bottom); - } if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) { c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY)); } @@ -2418,6 +2406,8 @@ public class GridLayout extends ViewGroup { * <li> {@code spec.span = [start, start + size]} </li> * <li> {@code spec.alignment = alignment} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start * @param size the size @@ -2433,9 +2423,13 @@ public class GridLayout extends ViewGroup { * <li> {@code spec.span = [start, start + 1]} </li> * <li> {@code spec.alignment = alignment} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start index * @param alignment the alignment + * + * @see #spec(int, int, Alignment) */ public static Spec spec(int start, Alignment alignment) { return spec(start, 1, alignment); @@ -2446,9 +2440,13 @@ public class GridLayout extends ViewGroup { * <ul> * <li> {@code spec.span = [start, start + size]} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start * @param size the size + * + * @see #spec(int, Alignment) */ public static Spec spec(int start, int size) { return spec(start, size, UNDEFINED_ALIGNMENT); @@ -2459,8 +2457,12 @@ public class GridLayout extends ViewGroup { * <ul> * <li> {@code spec.span = [start, start + 1]} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start index + * + * @see #spec(int, int) */ public static Spec spec(int start) { return spec(start, 1); @@ -2654,14 +2656,7 @@ public class GridLayout extends ViewGroup { @Override public int getAlignmentValue(View view, int viewSize, int mode) { int baseline = view.getBaseline(); - if (baseline == -1) { - return UNDEFINED; - } else { - if (mode == OPTICAL_BOUNDS) { - return baseline - view.getOpticalInsets().top; - } - return baseline; - } + return baseline == -1 ? UNDEFINED : baseline; } @Override diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 26c801f..1bbf4eb 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -30,6 +30,7 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -40,6 +41,9 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; +import java.io.IOException; +import java.io.InputStream; + /** * Displays an arbitrary image, such as an icon. The ImageView class * can load images from various sources (such as resources or content @@ -90,6 +94,9 @@ public class ImageView extends View { private int mBaseline = -1; private boolean mBaselineAlignBottom = false; + // AdjustViewBounds behavior will be in compatibility mode for older apps. + private boolean mAdjustViewBoundsCompat = false; + private static final ScaleType[] sScaleTypeArray = { ScaleType.MATRIX, ScaleType.FIT_XY, @@ -164,6 +171,8 @@ public class ImageView extends View { private void initImageView() { mMatrix = new Matrix(); mScaleType = ScaleType.FIT_CENTER; + mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <= + Build.VERSION_CODES.JELLY_BEAN_MR1; } @Override @@ -225,8 +234,15 @@ public class ImageView extends View { /** * Set this to true if you want the ImageView to adjust its bounds * to preserve the aspect ratio of its drawable. + * + * <p><strong>Note:</strong> If the application targets API level 17 or lower, + * adjustViewBounds will allow the drawable to shrink the view bounds, but not grow + * to fill available measured space in all cases. This is for compatibility with + * legacy {@link android.view.View.MeasureSpec MeasureSpec} and + * {@link android.widget.RelativeLayout RelativeLayout} behavior.</p> + * * @param adjustViewBounds Whether to adjust the bounds of this view - * to presrve the original aspect ratio of the drawable + * to preserve the original aspect ratio of the drawable. * * @see #getAdjustViewBounds() * @@ -546,13 +562,14 @@ public class ImageView extends View { /** Return the view's optional matrix. This is applied to the view's drawable when it is drawn. If there is not matrix, - this method will return null. - Do not change this matrix in place. If you want a different matrix - applied to the drawable, be sure to call setImageMatrix(). + this method will return an identity matrix. + Do not change this matrix in place but make a copy. + If you want a different matrix applied to the drawable, + be sure to call setImageMatrix(). */ public Matrix getImageMatrix() { if (mDrawMatrix == null) { - return Matrix.IDENTITY_MATRIX; + return new Matrix(Matrix.IDENTITY_MATRIX); } return mDrawMatrix; } @@ -635,20 +652,27 @@ public class ImageView extends View { } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { + InputStream stream = null; try { - d = Drawable.createFromStream( - mContext.getContentResolver().openInputStream(mUri), - null); + stream = mContext.getContentResolver().openInputStream(mUri); + d = Drawable.createFromStream(stream, null); } catch (Exception e) { Log.w("ImageView", "Unable to open content: " + mUri, e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + Log.w("ImageView", "Unable to close content: " + mUri, e); + } + } } - } else { + } else { d = Drawable.createFromPath(mUri.toString()); } if (d == null) { - System.out.println("resolveUri failed on bad bitmap uri: " - + mUri); + System.out.println("resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } @@ -792,6 +816,12 @@ public class ImageView extends View { if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; + + // Allow the width to outgrow its original estimate if height is fixed. + if (!resizeHeight && !mAdjustViewBoundsCompat) { + widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); + } + if (newWidth <= widthSize) { widthSize = newWidth; done = true; @@ -802,6 +832,13 @@ public class ImageView extends View { if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; + + // Allow the height to outgrow its original estimate if width is fixed. + if (!resizeWidth && !mAdjustViewBoundsCompat) { + heightSize = resolveAdjustedSize(newHeight, mMaxHeight, + heightMeasureSpec); + } + if (newHeight <= heightSize) { heightSize = newHeight; } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 36dd13c..bc57c36 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -1431,9 +1431,9 @@ public class LinearLayout extends ViewGroup { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mOrientation == VERTICAL) { - layoutVertical(); + layoutVertical(l, t, r, b); } else { - layoutHorizontal(); + layoutHorizontal(l, t, r, b); } } @@ -1444,15 +1444,19 @@ public class LinearLayout extends ViewGroup { * @see #getOrientation() * @see #setOrientation(int) * @see #onLayout(boolean, int, int, int, int) + * @param left + * @param top + * @param right + * @param bottom */ - void layoutVertical() { + void layoutVertical(int left, int top, int right, int bottom) { final int paddingLeft = mPaddingLeft; int childTop; int childLeft; // Where right end of child should go - final int width = mRight - mLeft; + final int width = right - left; int childRight = width - mPaddingRight; // Space available for child @@ -1466,12 +1470,12 @@ public class LinearLayout extends ViewGroup { switch (majorGravity) { case Gravity.BOTTOM: // mTotalLength contains the padding already - childTop = mPaddingTop + mBottom - mTop - mTotalLength; + childTop = mPaddingTop + bottom - top - mTotalLength; break; // mTotalLength contains the padding already case Gravity.CENTER_VERTICAL: - childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; + childTop = mPaddingTop + (bottom - top - mTotalLength) / 2; break; case Gravity.TOP: @@ -1534,8 +1538,12 @@ public class LinearLayout extends ViewGroup { * @see #getOrientation() * @see #setOrientation(int) * @see #onLayout(boolean, int, int, int, int) + * @param left + * @param top + * @param right + * @param bottom */ - void layoutHorizontal() { + void layoutHorizontal(int left, int top, int right, int bottom) { final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; @@ -1543,7 +1551,7 @@ public class LinearLayout extends ViewGroup { int childLeft; // Where bottom of child should go - final int height = mBottom - mTop; + final int height = bottom - top; int childBottom = height - mPaddingBottom; // Space available for child @@ -1563,12 +1571,12 @@ public class LinearLayout extends ViewGroup { switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { case Gravity.RIGHT: // mTotalLength contains the padding already - childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; + childLeft = mPaddingLeft + right - left - mTotalLength; break; case Gravity.CENTER_HORIZONTAL: // mTotalLength contains the padding already - childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; + childLeft = mPaddingLeft + (right - left - mTotalLength) / 2; break; case Gravity.LEFT: diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index f76ab2b..ee1bf18 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -149,7 +149,7 @@ public class MediaController extends FrameLayout { private void initFloatingWindowLayout() { mDecorLayoutParams = new WindowManager.LayoutParams(); WindowManager.LayoutParams p = mDecorLayoutParams; - p.gravity = Gravity.TOP; + p.gravity = Gravity.TOP | Gravity.LEFT; p.height = LayoutParams.WRAP_CONTENT; p.x = 0; p.format = PixelFormat.TRANSLUCENT; @@ -167,9 +167,15 @@ public class MediaController extends FrameLayout { int [] anchorPos = new int[2]; mAnchor.getLocationOnScreen(anchorPos); + // we need to know the size of the controller so we can properly position it + // within its space + mDecor.measure(MeasureSpec.makeMeasureSpec(mAnchor.getWidth(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(mAnchor.getHeight(), MeasureSpec.AT_MOST)); + WindowManager.LayoutParams p = mDecorLayoutParams; p.width = mAnchor.getWidth(); - p.y = anchorPos[1] + mAnchor.getHeight(); + p.x = anchorPos[0] + (mAnchor.getWidth() - p.width) / 2; + p.y = anchorPos[1] + mAnchor.getHeight() - mDecor.getMeasuredHeight(); } // This is called whenever mAnchor's layout bound changes @@ -204,6 +210,8 @@ public class MediaController extends FrameLayout { /** * Set the view that acts as the anchor for the control view. * This can for example be a VideoView, or your Activity's main view. + * When VideoView calls this method, it will use the VideoView's parent + * as the anchor. * @param view The view to which to anchor the controller when it is visible. */ public void setAnchorView(View view) { diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index ea50e2e..d816200 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -38,10 +38,7 @@ import android.graphics.drawable.shapes.Shape; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; -import android.util.Pool; -import android.util.Poolable; -import android.util.PoolableManager; -import android.util.Pools; +import android.util.Pools.SynchronizedPool; import android.view.Gravity; import android.view.RemotableViewMethod; import android.view.View; @@ -226,6 +223,8 @@ public class ProgressBar extends View { private boolean mAttached; private boolean mRefreshIsPosted; + boolean mMirrorForRtl = false; + private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>(); private AccessibilityEventSender mAccessibilityEventSender; @@ -305,6 +304,8 @@ public class ProgressBar extends View { setIndeterminate(mOnlyIndeterminate || a.getBoolean( R.styleable.ProgressBar_indeterminate, mIndeterminate)); + mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); + a.recycle(); } @@ -604,33 +605,20 @@ public class ProgressBar extends View { } } - private static class RefreshData implements Poolable<RefreshData> { + private static class RefreshData { + private static final int POOL_MAX = 24; + private static final SynchronizedPool<RefreshData> sPool = + new SynchronizedPool<RefreshData>(POOL_MAX); + public int id; public int progress; public boolean fromUser; - - private RefreshData mNext; - private boolean mIsPooled; - - private static final int POOL_MAX = 24; - private static final Pool<RefreshData> sPool = Pools.synchronizedPool( - Pools.finitePool(new PoolableManager<RefreshData>() { - @Override - public RefreshData newInstance() { - return new RefreshData(); - } - - @Override - public void onAcquired(RefreshData element) { - } - - @Override - public void onReleased(RefreshData element) { - } - }, POOL_MAX)); public static RefreshData obtain(int id, int progress, boolean fromUser) { RefreshData rd = sPool.acquire(); + if (rd == null) { + rd = new RefreshData(); + } rd.id = id; rd.progress = progress; rd.fromUser = fromUser; @@ -640,28 +628,8 @@ public class ProgressBar extends View { public void recycle() { sPool.release(this); } - - @Override - public void setNextPoolable(RefreshData element) { - mNext = element; - } - - @Override - public RefreshData getNextPoolable() { - return mNext; - } - - @Override - public boolean isPooled() { - return mIsPooled; - } - - @Override - public void setPooled(boolean isPooled) { - mIsPooled = isPooled; - } } - + private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, boolean callBackToApp) { float scale = mMax > 0 ? (float) progress / (float) mMax : 0; @@ -1040,7 +1008,7 @@ public class ProgressBar extends View { } } } - if (isLayoutRtl()) { + if (isLayoutRtl() && mMirrorForRtl) { int tempLeft = left; left = w - right; right = w - tempLeft; @@ -1062,7 +1030,7 @@ public class ProgressBar extends View { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation canvas.save(); - if(isLayoutRtl()) { + if(isLayoutRtl() && mMirrorForRtl) { canvas.translate(getWidth() - mPaddingRight, mPaddingTop); canvas.scale(-1.0f, 1.0f); } else { diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index 786afe2..368f6ad 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -27,6 +27,7 @@ import android.database.Cursor; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; @@ -50,6 +51,7 @@ public class QuickContactBadge extends ImageView implements OnClickListener { private Drawable mOverlay; private QueryHandler mQueryHandler; private Drawable mDefaultAvatar; + private Bundle mExtras = null; protected String[] mExcludeMimes = null; @@ -58,6 +60,8 @@ public class QuickContactBadge extends ImageView implements OnClickListener { static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2; static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3; + static final private String EXTRA_URI_CONTENT = "uri_content"; + static final String[] EMAIL_LOOKUP_PROJECTION = new String[] { RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY, @@ -175,7 +179,26 @@ public class QuickContactBadge extends ImageView implements OnClickListener { * until this view is clicked. */ public void assignContactFromEmail(String emailAddress, boolean lazyLookup) { + assignContactFromEmail(emailAddress, lazyLookup, null); + } + + /** + * Assign a contact based on an email address. This should only be used when + * the contact's URI is not available, as an extra query will have to be + * performed to lookup the URI based on the email. + + @param emailAddress The email address of the contact. + @param lazyLookup If this is true, the lookup query will not be performed + until this view is clicked. + @param extras A bundle of extras to populate the contact edit page with if the contact + is not found and the user chooses to add the email address to an existing contact or + create a new contact. Uses the same string constants as those found in + {@link android.provider.ContactsContract.Intents.Insert} + */ + + public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) { mContactEmail = emailAddress; + mExtras = extras; if (!lazyLookup) { mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null, Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), @@ -186,6 +209,7 @@ public class QuickContactBadge extends ImageView implements OnClickListener { } } + /** * Assign a contact based on a phone number. This should only be used when * the contact's URI is not available, as an extra query will have to be @@ -196,7 +220,25 @@ public class QuickContactBadge extends ImageView implements OnClickListener { * until this view is clicked. */ public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) { + assignContactFromPhone(phoneNumber, lazyLookup, new Bundle()); + } + + /** + * Assign a contact based on a phone number. This should only be used when + * the contact's URI is not available, as an extra query will have to be + * performed to lookup the URI based on the phone number. + * + * @param phoneNumber The phone number of the contact. + * @param lazyLookup If this is true, the lookup query will not be performed + * until this view is clicked. + * @param extras A bundle of extras to populate the contact edit page with if the contact + * is not found and the user chooses to add the phone number to an existing contact or + * create a new contact. Uses the same string constants as those found in + * {@link android.provider.ContactsContract.Intents.Insert} + */ + public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) { mContactPhone = phoneNumber; + mExtras = extras; if (!lazyLookup) { mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null, Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), @@ -213,15 +255,21 @@ public class QuickContactBadge extends ImageView implements OnClickListener { @Override public void onClick(View v) { + // If contact has been assigned, mExtras should no longer be null, but do a null check + // anyway just in case assignContactFromPhone or Email was called with a null bundle or + // wasn't assigned previously. + final Bundle extras = (mExtras == null) ? new Bundle() : mExtras; if (mContactUri != null) { QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri, QuickContact.MODE_LARGE, mExcludeMimes); } else if (mContactEmail != null) { - mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, mContactEmail, + extras.putString(EXTRA_URI_CONTENT, mContactEmail); + mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras, Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), EMAIL_LOOKUP_PROJECTION, null, null, null); } else if (mContactPhone != null) { - mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, mContactPhone, + extras.putString(EXTRA_URI_CONTENT, mContactPhone); + mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras, Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), PHONE_LOOKUP_PROJECTION, null, null, null); } else { @@ -262,12 +310,12 @@ public class QuickContactBadge extends ImageView implements OnClickListener { Uri lookupUri = null; Uri createUri = null; boolean trigger = false; - + Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle(); try { switch(token) { case TOKEN_PHONE_LOOKUP_AND_TRIGGER: trigger = true; - createUri = Uri.fromParts("tel", (String)cookie, null); + createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null); //$FALL-THROUGH$ case TOKEN_PHONE_LOOKUP: { @@ -281,7 +329,8 @@ public class QuickContactBadge extends ImageView implements OnClickListener { } case TOKEN_EMAIL_LOOKUP_AND_TRIGGER: trigger = true; - createUri = Uri.fromParts("mailto", (String)cookie, null); + createUri = Uri.fromParts("mailto", + extras.getString(EXTRA_URI_CONTENT), null); //$FALL-THROUGH$ case TOKEN_EMAIL_LOOKUP: { @@ -309,6 +358,10 @@ public class QuickContactBadge extends ImageView implements OnClickListener { } else if (createUri != null) { // Prompt user to add this person to contacts final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri); + if (extras != null) { + extras.remove(EXTRA_URI_CONTENT); + intent.putExtras(extras); + } getContext().startActivity(intent); } } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 27fda24..deec41c 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -29,16 +29,15 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; +import android.os.Build; import android.util.AttributeSet; -import android.util.Pool; -import android.util.Poolable; -import android.util.PoolableManager; -import android.util.Pools; +import android.util.Pools.SimplePool; import android.util.SparseArray; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; @@ -56,6 +55,21 @@ import static android.util.Log.d; * {@link #ALIGN_PARENT_BOTTOM}. * </p> * + * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by + * a measurement bug that could cause child views to be measured with incorrect + * {@link android.view.View.MeasureSpec MeasureSpec} values. (See + * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec} + * for more details.) This was triggered when a RelativeLayout container was placed in + * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view + * not equipped to properly measure with the MeasureSpec mode + * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout, + * this would silently work anyway as RelativeLayout would pass a very large + * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p> + * + * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code> + * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK + * version 18 or newer will receive the correct behavior</p> + * * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative * Layout</a> guide.</p> * @@ -202,18 +216,34 @@ public class RelativeLayout extends ViewGroup { private View[] mSortedVerticalChildren = new View[0]; private final DependencyGraph mGraph = new DependencyGraph(); + // Compatibility hack. Old versions of the platform had problems + // with MeasureSpec value overflow and RelativeLayout was one source of them. + // Some apps came to rely on them. :( + private boolean mAllowBrokenMeasureSpecs = false; + + private int mDisplayWidth; + public RelativeLayout(Context context) { super(context); + mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <= + Build.VERSION_CODES.JELLY_BEAN_MR1; + getDisplayWidth(); } public RelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); initFromAttributes(context, attrs); + mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <= + Build.VERSION_CODES.JELLY_BEAN_MR1; + getDisplayWidth(); } public RelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initFromAttributes(context, attrs); + mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <= + Build.VERSION_CODES.JELLY_BEAN_MR1; + getDisplayWidth(); } private void initFromAttributes(Context context, AttributeSet attrs) { @@ -223,6 +253,11 @@ public class RelativeLayout extends ViewGroup { a.recycle(); } + private void getDisplayWidth() { + WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); + mDisplayWidth = wm.getDefaultDisplay().getWidth(); + } + @Override public boolean shouldDelayChildPressedState() { return false; @@ -414,51 +449,28 @@ public class RelativeLayout extends ViewGroup { final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; + // We need to know our size for doing the correct computation of children positioning in RTL + // mode but there is no practical way to get it instead of running the code below. + // So, instead of running the code twice, we just set the width to the "display width" + // before the computation and then, as a last pass, we will update their real position with + // an offset equals to "displayWidth - width". + final int layoutDirection = getLayoutDirection(); + if (isLayoutRtl() && myWidth == -1) { + myWidth = mDisplayWidth; + } + View[] views = mSortedHorizontalChildren; int count = views.length; - // We need to know our size for doing the correct computation of positioning in RTL mode - if (isLayoutRtl() && (myWidth == -1 || isWrapContentWidth)) { - int w = getPaddingStart() + getPaddingEnd(); - final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - for (int i = 0; i < count; i++) { - View child = views[i]; - if (child.getVisibility() != GONE) { - LayoutParams params = (LayoutParams) child.getLayoutParams(); - // Would be similar to a call to measureChildHorizontal(child, params, -1, myHeight) - // but we cannot change for now the behavior of measureChildHorizontal() for - // taking care or a "-1" for "mywidth" so use here our own version of that code. - int childHeightMeasureSpec; - if (params.width == LayoutParams.MATCH_PARENT) { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); - } else { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); - } - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - - w += child.getMeasuredWidth(); - w += params.leftMargin + params.rightMargin; - } - } - if (myWidth == -1) { - // Easy case: "myWidth" was undefined before so use the width we have just computed - myWidth = w; - } else { - // "myWidth" was defined before, so take the min of it and the computed width if it - // is a non null one - if (w > 0) { - myWidth = Math.min(myWidth, w); - } - } - } - for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); + int[] rules = params.getRules(layoutDirection); - applyHorizontalSizeRules(params, myWidth); + applyHorizontalSizeRules(params, myWidth, rules); measureChildHorizontal(child, params, myWidth, myHeight); + if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { offsetHorizontalAxis = true; } @@ -480,7 +492,11 @@ public class RelativeLayout extends ViewGroup { } if (isWrapContentWidth) { - width = Math.max(width, params.mRight); + if (isLayoutRtl()) { + width = Math.max(width, myWidth - params.mLeft); + } else { + width = Math.max(width, params.mRight); + } } if (isWrapContentHeight) { @@ -519,8 +535,6 @@ public class RelativeLayout extends ViewGroup { } } - final int layoutDirection = getLayoutDirection(); - if (isWrapContentWidth) { // Width already has left padding in it since it was calculated by looking at // the right of each child view @@ -610,6 +624,19 @@ public class RelativeLayout extends ViewGroup { } } + if (isLayoutRtl()) { + final int offsetWidth = myWidth - width; + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + params.mLeft -= offsetWidth; + params.mRight -= offsetWidth; + } + } + + } + setMeasuredDimension(width, height); } @@ -673,7 +700,17 @@ public class RelativeLayout extends ViewGroup { mPaddingLeft, mPaddingRight, myWidth); int childHeightMeasureSpec; - if (params.width == LayoutParams.MATCH_PARENT) { + if (myHeight < 0 && !mAllowBrokenMeasureSpecs) { + if (params.height >= 0) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + params.height, MeasureSpec.EXACTLY); + } else { + // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement + // is code for, "we got an unspecified mode in the RelativeLayout's measurespec." + // Carry it forward. + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + } else if (params.width == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); @@ -700,6 +737,16 @@ public class RelativeLayout extends ViewGroup { private int getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize) { + if (mySize < 0 && !mAllowBrokenMeasureSpecs) { + if (childSize >= 0) { + return MeasureSpec.makeMeasureSpec(childSize, MeasureSpec.EXACTLY); + } + // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement + // is code for, "we got an unspecified mode in the RelativeLayout's measurespec." + // Carry it forward. + return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + int childSpecMode = 0; int childSpecSize = 0; @@ -826,9 +873,7 @@ public class RelativeLayout extends ViewGroup { return rules[ALIGN_PARENT_BOTTOM] != 0; } - private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) { - final int layoutDirection = getLayoutDirection(); - int[] rules = childParams.getRules(layoutDirection); + private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) { RelativeLayout.LayoutParams anchorParams; // -1 indicated a "soft requirement" in that direction. For example: @@ -991,7 +1036,7 @@ public class RelativeLayout extends ViewGroup { return -1; } - private void centerHorizontal(View child, LayoutParams params, int myWidth) { + private static void centerHorizontal(View child, LayoutParams params, int myWidth) { int childWidth = child.getMeasuredWidth(); int left = (myWidth - childWidth) / 2; @@ -999,7 +1044,7 @@ public class RelativeLayout extends ViewGroup { params.mRight = left + childWidth; } - private void centerVertical(View child, LayoutParams params, int myHeight) { + private static void centerVertical(View child, LayoutParams params, int myHeight) { int childHeight = child.getMeasuredHeight(); int top = (myHeight - childHeight) / 2; @@ -1193,6 +1238,7 @@ public class RelativeLayout extends ViewGroup { com.android.internal.R.styleable.RelativeLayout_Layout); final int[] rules = mRules; + //noinspection MismatchedReadAndWriteOfArray final int[] initialRules = mInitialRules; final int N = a.getIndexCount(); @@ -1271,9 +1317,7 @@ public class RelativeLayout extends ViewGroup { } } - for (int n = LEFT_OF; n < VERB_COUNT; n++) { - initialRules[n] = rules[n]; - } + System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT); a.recycle(); } @@ -1364,9 +1408,7 @@ public class RelativeLayout extends ViewGroup { private void resolveRules(int layoutDirection) { final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL); // Reset to initial state - for (int n = LEFT_OF; n < VERB_COUNT; n++) { - mRules[n] = mInitialRules[n]; - } + System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT); // Apply rules depending on direction if (mRules[ALIGN_START] != 0) { mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START]; @@ -1655,7 +1697,7 @@ public class RelativeLayout extends ViewGroup { * * A node with no dependent is considered a root of the graph. */ - static class Node implements Poolable<Node> { + static class Node { /** * The view representing this node in the layout. */ @@ -1678,43 +1720,14 @@ public class RelativeLayout extends ViewGroup { // The pool is static, so all nodes instances are shared across // activities, that's why we give it a rather high limit private static final int POOL_LIMIT = 100; - private static final Pool<Node> sPool = Pools.synchronizedPool( - Pools.finitePool(new PoolableManager<Node>() { - public Node newInstance() { - return new Node(); - } - - public void onAcquired(Node element) { - } - - public void onReleased(Node element) { - } - }, POOL_LIMIT) - ); - - private Node mNext; - private boolean mIsPooled; - - public void setNextPoolable(Node element) { - mNext = element; - } - - public Node getNextPoolable() { - return mNext; - } - - public boolean isPooled() { - return mIsPooled; - } - - public void setPooled(boolean isPooled) { - mIsPooled = isPooled; - } + private static final SimplePool<Node> sPool = new SimplePool<Node>(POOL_LIMIT); static Node acquire(View view) { - final Node node = sPool.acquire(); + Node node = sPool.acquire(); + if (node == null) { + node = new Node(); + } node.view = view; - return node; } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 8d1be53..79fc51e 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -53,7 +53,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; - /** * A class that describes a view hierarchy that can be displayed in * another process. The hierarchy is inflated from a layout resource @@ -340,7 +339,7 @@ public class RemoteViews implements Parcelable, Filter { if (target == null) return; if (!mIsWidgetCollectionChild) { - Log.e("RemoteViews", "The method setOnClickFillInIntent is available " + + Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " + "only from RemoteViewsFactory (ie. on collection items)."); return; } @@ -359,13 +358,13 @@ public class RemoteViews implements Parcelable, Filter { if (parent instanceof AppWidgetHostView || parent == null) { // Somehow they've managed to get this far without having // and AdapterView as a parent. - Log.e("RemoteViews", "Collection item doesn't have AdapterView parent"); + Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent"); return; } // Insure that a template pending intent has been set on an ancestor if (!(parent.getTag() instanceof PendingIntent)) { - Log.e("RemoteViews", "Attempting setOnClickFillInIntent without" + + Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" + " calling setPendingIntentTemplate on parent."); return; } @@ -472,7 +471,7 @@ public class RemoteViews implements Parcelable, Filter { av.setOnItemClickListener(listener); av.setTag(pendingIntentTemplate); } else { - Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" + + Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" + "an AdapterView (id: " + viewId + ")"); return; } @@ -487,6 +486,88 @@ public class RemoteViews implements Parcelable, Filter { public final static int TAG = 8; } + private class SetRemoteViewsAdapterList extends Action { + public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) { + this.viewId = id; + this.list = list; + this.viewTypeCount = viewTypeCount; + } + + public SetRemoteViewsAdapterList(Parcel parcel) { + viewId = parcel.readInt(); + viewTypeCount = parcel.readInt(); + int count = parcel.readInt(); + list = new ArrayList<RemoteViews>(); + + for (int i = 0; i < count; i++) { + RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel); + list.add(rv); + } + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(viewTypeCount); + + if (list == null || list.size() == 0) { + dest.writeInt(0); + } else { + int count = list.size(); + dest.writeInt(count); + for (int i = 0; i < count; i++) { + RemoteViews rv = list.get(i); + rv.writeToParcel(dest, flags); + } + } + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + final View target = root.findViewById(viewId); + if (target == null) return; + + // Ensure that we are applying to an AppWidget root + if (!(rootParent instanceof AppWidgetHostView)) { + Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " + + "AppWidgets (root id: " + viewId + ")"); + return; + } + // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it + if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { + Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " + + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); + return; + } + + if (target instanceof AbsListView) { + AbsListView v = (AbsListView) target; + Adapter a = v.getAdapter(); + if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { + ((RemoteViewsListAdapter) a).setViewsList(list); + } else { + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + } + } else if (target instanceof AdapterViewAnimator) { + AdapterViewAnimator v = (AdapterViewAnimator) target; + Adapter a = v.getAdapter(); + if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { + ((RemoteViewsListAdapter) a).setViewsList(list); + } else { + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + } + } + } + + public String getActionName() { + return "SetRemoteViewsAdapterList"; + } + + int viewTypeCount; + ArrayList<RemoteViews> list; + public final static int TAG = 15; + } + private class SetRemoteViewsAdapterIntent extends Action { public SetRemoteViewsAdapterIntent(int id, Intent intent) { this.viewId = id; @@ -511,13 +592,13 @@ public class RemoteViews implements Parcelable, Filter { // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { - Log.e("RemoteViews", "SetRemoteViewsAdapterIntent action can only be used for " + + Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " + "AppWidgets (root id: " + viewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { - Log.e("RemoteViews", "Cannot setRemoteViewsAdapter on a view which is not " + + Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); return; } @@ -585,7 +666,7 @@ public class RemoteViews implements Parcelable, Filter { // If the view is an AdapterView, setting a PendingIntent on click doesn't make much // sense, do they mean to set a PendingIntent template for the AdapterView's children? if (mIsWidgetCollectionChild) { - Log.w("RemoteViews", "Cannot setOnClickPendingIntent for collection item " + + Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " + "(id: " + viewId + ")"); ApplicationInfo appInfo = root.getContext().getApplicationInfo(); @@ -772,7 +853,7 @@ public class RemoteViews implements Parcelable, Filter { try { //noinspection ConstantIfStatement if (false) { - Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " + Log.d(LOG_TAG, "view: " + klass.getName() + " calling method: " + this.methodName + "()"); } method.invoke(view); @@ -945,7 +1026,7 @@ public class RemoteViews implements Parcelable, Filter { this.type = in.readInt(); //noinspection ConstantIfStatement if (false) { - Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId) + Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); } @@ -1012,7 +1093,7 @@ public class RemoteViews implements Parcelable, Filter { out.writeInt(this.type); //noinspection ConstantIfStatement if (false) { - Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId) + Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); } @@ -1139,7 +1220,7 @@ public class RemoteViews implements Parcelable, Filter { try { //noinspection ConstantIfStatement if (false) { - Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " + Log.d(LOG_TAG, "view: " + klass.getName() + " calling method: " + this.methodName + "(" + param.getName() + ") with " + (this.value == null ? "null" : this.value.getClass().getName())); } @@ -1562,6 +1643,9 @@ public class RemoteViews implements Parcelable, Filter { case BitmapReflectionAction.TAG: mActions.add(new BitmapReflectionAction(parcel)); break; + case SetRemoteViewsAdapterList.TAG: + mActions.add(new SetRemoteViewsAdapterList(parcel)); + break; default: throw new ActionException("Tag " + tag + " not found"); } @@ -1982,7 +2066,7 @@ public class RemoteViews implements Parcelable, Filter { * * @param appWidgetId The id of the app widget which contains the specified view. (This * parameter is ignored in this deprecated method) - * @param viewId The id of the {@link AbsListView} + * @param viewId The id of the {@link AdapterView} * @param intent The intent of the service which will be * providing data to the RemoteViewsAdapter * @deprecated This method has been deprecated. See @@ -1997,7 +2081,7 @@ public class RemoteViews implements Parcelable, Filter { * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. * Can only be used for App Widgets. * - * @param viewId The id of the {@link AbsListView} + * @param viewId The id of the {@link AdapterView} * @param intent The intent of the service which will be * providing data to the RemoteViewsAdapter */ @@ -2006,6 +2090,29 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView, + * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}. + * This is a simpler but less flexible approach to populating collection widgets. Its use is + * encouraged for most scenarios, as long as the total memory within the list of RemoteViews + * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link + * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still + * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}. + * + * This API is supported in the compatibility library for previous API levels, see + * RemoteViewsCompat. + * + * @param viewId The id of the {@link AdapterView} + * @param list The list of RemoteViews which will populate the view specified by viewId. + * @param viewTypeCount The maximum number of unique layout id's used to construct the list of + * RemoteViews. This count cannot change during the life-cycle of a given widget, so this + * parameter should account for the maximum possible number of types that may appear in the + * See {@link Adapter#getViewTypeCount()}. + */ + public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) { + addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount)); + } + + /** * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}. * * @param viewId The id of the view to change diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java new file mode 100644 index 0000000..e490458 --- /dev/null +++ b/core/java/android/widget/RemoteViewsListAdapter.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +/** + * @hide + */ +public class RemoteViewsListAdapter extends BaseAdapter { + + private Context mContext; + private ArrayList<RemoteViews> mRemoteViewsList; + private ArrayList<Integer> mViewTypes = new ArrayList<Integer>(); + private int mViewTypeCount; + + public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews, + int viewTypeCount) { + mContext = context; + mRemoteViewsList = remoteViews; + mViewTypeCount = viewTypeCount; + init(); + } + + public void setViewsList(ArrayList<RemoteViews> remoteViews) { + mRemoteViewsList = remoteViews; + init(); + notifyDataSetChanged(); + } + + private void init() { + if (mRemoteViewsList == null) return; + + mViewTypes.clear(); + for (RemoteViews rv: mRemoteViewsList) { + if (!mViewTypes.contains(rv.getLayoutId())) { + mViewTypes.add(rv.getLayoutId()); + } + } + + if (mViewTypes.size() > mViewTypeCount || mViewTypeCount < 1) { + throw new RuntimeException("Invalid view type count -- view type count must be >= 1" + + "and must be as large as the total number of distinct view types"); + } + } + + @Override + public int getCount() { + if (mRemoteViewsList != null) { + return mRemoteViewsList.size(); + } else { + return 0; + } + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (position < getCount()) { + RemoteViews rv = mRemoteViewsList.get(position); + rv.setIsWidgetCollectionChild(true); + View v; + if (convertView != null && rv != null && + convertView.getId() == rv.getLayoutId()) { + v = convertView; + rv.reapply(mContext, v); + } else { + v = rv.apply(mContext, parent); + } + return v; + } else { + return null; + } + } + + @Override + public int getItemViewType(int position) { + if (position < getCount()) { + int layoutId = mRemoteViewsList.get(position).getLayoutId(); + return mViewTypes.indexOf(layoutId); + } else { + return 0; + } + } + + public int getViewTypeCount() { + return mViewTypeCount; + } + + @Override + public boolean hasStableIds() { + return false; + } +} diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index cd8638d..0281602 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -1247,6 +1247,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { */ @Override public void onActionViewCollapsed() { + setQuery("", false); clearFocus(); updateViewsVisibility(true); mQueryTextView.setImeOptions(mCollapsedImeOptions); diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java index 2737f94..a6486a8 100644 --- a/core/java/android/widget/SeekBar.java +++ b/core/java/android/widget/SeekBar.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -33,7 +34,7 @@ import android.view.accessibility.AccessibilityNodeInfo; * * @attr ref android.R.styleable#SeekBar_thumb */ -public class SeekBar extends AbsSeekBar { +public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> { /** * A callback that notifies clients when the progress level has been @@ -69,8 +70,9 @@ public class SeekBar extends AbsSeekBar { void onStopTrackingTouch(SeekBar seekBar); } + private ValueModel<Integer> mValueModel = ValueModel.EMPTY; private OnSeekBarChangeListener mOnSeekBarChangeListener; - + public SeekBar(Context context) { this(context, null); } @@ -89,9 +91,23 @@ public class SeekBar extends AbsSeekBar { if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser); + if (fromUser) { + mValueModel.set(getProgress()); + } } } + @Override + public ValueModel<Integer> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<Integer> valueModel) { + mValueModel = valueModel; + setProgress(mValueModel.get()); + } + /** * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also * provides notifications of when the user starts and stops a touch gesture within the SeekBar. diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 74ea038..9e7f97e 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -109,7 +109,7 @@ public class SpellChecker implements SpellCheckerSessionListener { mIds = new int[size]; mSpellCheckSpans = new SpellCheckSpan[size]; - setLocale(mTextView.getTextServicesLocale()); + setLocale(mTextView.getSpellCheckerLocale()); mCookie = hashCode(); } @@ -120,7 +120,8 @@ public class SpellChecker implements SpellCheckerSessionListener { mTextServicesManager = (TextServicesManager) mTextView.getContext(). getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); if (!mTextServicesManager.isSpellCheckerEnabled() - || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) { + || mCurrentLocale == null + || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) { mSpellCheckerSession = null; } else { mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession( @@ -146,8 +147,10 @@ public class SpellChecker implements SpellCheckerSessionListener { resetSession(); - // Change SpellParsers' wordIterator locale - mWordIterator = new WordIterator(locale); + if (locale != null) { + // Change SpellParsers' wordIterator locale + mWordIterator = new WordIterator(locale); + } // This class is the listener for locale change: warn other locale-aware objects mTextView.onLocaleChanged(); @@ -222,9 +225,9 @@ public class SpellChecker implements SpellCheckerSessionListener { if (DBG) { Log.d(TAG, "Start spell-checking: " + start + ", " + end); } - final Locale locale = mTextView.getTextServicesLocale(); + final Locale locale = mTextView.getSpellCheckerLocale(); final boolean isSessionActive = isSessionActive(); - if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) { + if (locale == null || mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) { setLocale(locale); // Re-check the entire text start = 0; diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 925864c..fa64fd3 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -25,6 +25,8 @@ import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; @@ -697,6 +699,69 @@ public class Spinner extends AbsSpinner implements OnClickListener { return width; } + @Override + public Parcelable onSaveInstanceState() { + final SavedState ss = new SavedState(super.onSaveInstanceState()); + ss.showDropdown = mPopup != null && mPopup.isShowing(); + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + + super.onRestoreInstanceState(ss.getSuperState()); + + if (ss.showDropdown) { + ViewTreeObserver vto = getViewTreeObserver(); + if (vto != null) { + final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (!mPopup.isShowing()) { + mPopup.show(); + } + final ViewTreeObserver vto = getViewTreeObserver(); + if (vto != null) { + vto.removeOnGlobalLayoutListener(this); + } + } + }; + vto.addOnGlobalLayoutListener(listener); + } + } + } + + static class SavedState extends AbsSpinner.SavedState { + boolean showDropdown; + + SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + showDropdown = in.readByte() != 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeByte((byte) (showDropdown ? 1 : 0)); + } + + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + /** * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance * into a ListAdapter.</p> @@ -941,8 +1006,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { mHintText = hintText; } - @Override - public void show() { + void computeContentWidth() { final Drawable background = getBackground(); int hOffset = 0; if (background != null) { @@ -955,6 +1019,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { final int spinnerPaddingLeft = Spinner.this.getPaddingLeft(); final int spinnerPaddingRight = Spinner.this.getPaddingRight(); final int spinnerWidth = Spinner.this.getWidth(); + if (mDropDownWidth == WRAP_CONTENT) { int contentWidth = measureContentWidth( (SpinnerAdapter) mAdapter, getBackground()); @@ -977,11 +1042,25 @@ public class Spinner extends AbsSpinner implements OnClickListener { hOffset += spinnerPaddingLeft; } setHorizontalOffset(hOffset); + } + + @Override + public void show() { + final boolean wasShowing = isShowing(); + + computeContentWidth(); + setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); super.show(); getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); setSelection(Spinner.this.getSelectedItemPosition()); + if (wasShowing) { + // Skip setting up the layout/dismiss listener below. If we were previously + // showing it will still stick around. + return; + } + // Make sure we hide if our anchor goes away. // TODO: This might be appropriate to push all the way down to PopupWindow, // but it may have other side effects to investigate first. (Text editing handles, etc.) @@ -992,6 +1071,12 @@ public class Spinner extends AbsSpinner implements OnClickListener { public void onGlobalLayout() { if (!Spinner.this.isVisibleToUser()) { dismiss(); + } else { + computeContentWidth(); + + // Use super.show here to update; we don't want to move the selected + // position or adjust other things that would be reset otherwise. + DropdownPopup.super.show(); } } }; diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index 399b4fa..f4b2ce0 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -445,7 +445,7 @@ public class TableLayout extends LinearLayout { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // enforce vertical layout - layoutVertical(); + layoutVertical(l, t, r, b); } /** diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index 35927e0..fe3631a 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -120,7 +120,7 @@ public class TableRow extends LinearLayout { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // enforce horizontal layout - layoutHorizontal(); + layoutHorizontal(l, t, r, b); } /** diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index 290d9b5..5bf21c0 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -386,6 +386,16 @@ public class TextClock extends TextView { } /** + * Returns the current format string. Always valid after constructor has + * finished, and will never be {@code null}. + * + * @hide + */ + public CharSequence getFormat() { + return mFormat; + } + + /** * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} * depending on whether the user has selected 24-hour format. * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 22bfadb..a1ced6e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; @@ -510,7 +511,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private InputFilter[] mFilters = NO_FILTERS; - private volatile Locale mCurrentTextServicesLocaleCache; + private volatile Locale mCurrentSpellCheckerLocaleCache; private final ReentrantLock mCurrentTextServicesLocaleLock = new ReentrantLock(); // It is possible to have a selection even when mEditor is null (programmatically set, like when @@ -606,6 +607,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int typefaceIndex = -1; int styleIndex = -1; boolean allCaps = false; + int shadowcolor = 0; + float dx = 0, dy = 0, r = 0; final Resources.Theme theme = context.getTheme(); @@ -666,6 +669,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_textAllCaps: allCaps = appearance.getBoolean(attr, false); break; + + case com.android.internal.R.styleable.TextAppearance_shadowColor: + shadowcolor = a.getInt(attr, 0); + break; + + case com.android.internal.R.styleable.TextAppearance_shadowDx: + dx = a.getFloat(attr, 0); + break; + + case com.android.internal.R.styleable.TextAppearance_shadowDy: + dy = a.getFloat(attr, 0); + break; + + case com.android.internal.R.styleable.TextAppearance_shadowRadius: + r = a.getFloat(attr, 0); + break; } } @@ -689,8 +708,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int maxlength = -1; CharSequence text = ""; CharSequence hint = null; - int shadowcolor = 0; - float dx = 0, dy = 0, r = 0; boolean password = false; int inputType = EditorInfo.TYPE_NULL; @@ -2330,6 +2347,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setTypefaceFromAttrs(familyName, typefaceIndex, styleIndex); + final int shadowcolor = appearance.getInt( + com.android.internal.R.styleable.TextAppearance_shadowColor, 0); + if (shadowcolor != 0) { + final float dx = appearance.getFloat( + com.android.internal.R.styleable.TextAppearance_shadowDx, 0); + final float dy = appearance.getFloat( + com.android.internal.R.styleable.TextAppearance_shadowDy, 0); + final float r = appearance.getFloat( + com.android.internal.R.styleable.TextAppearance_shadowRadius, 0); + + setShadowLayer(r, dx, dy, shadowcolor); + } + if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps, false)) { setTransformationMethod(new AllCapsTransformationMethod(getContext())); @@ -4349,6 +4379,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ///////////////////////////////////////////////////////////////////////// + private int getBoxHeight(Layout l) { + Insets opticalInsets = isLayoutModeOptical(mParent) ? getOpticalInsets() : Insets.NONE; + int padding = (l == mHintLayout) ? + getCompoundPaddingTop() + getCompoundPaddingBottom() : + getExtendedPaddingTop() + getExtendedPaddingBottom(); + return getMeasuredHeight() - padding + opticalInsets.top + opticalInsets.bottom; + } + int getVerticalOffset(boolean forceNormal) { int voffset = 0; final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; @@ -4359,15 +4397,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (gravity != Gravity.TOP) { - int boxht; - - if (l == mHintLayout) { - boxht = getMeasuredHeight() - getCompoundPaddingTop() - - getCompoundPaddingBottom(); - } else { - boxht = getMeasuredHeight() - getExtendedPaddingTop() - - getExtendedPaddingBottom(); - } + int boxht = getBoxHeight(l); int textht = l.getHeight(); if (textht < boxht) { @@ -4390,15 +4420,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (gravity != Gravity.BOTTOM) { - int boxht; - - if (l == mHintLayout) { - boxht = getMeasuredHeight() - getCompoundPaddingTop() - - getCompoundPaddingBottom(); - } else { - boxht = getMeasuredHeight() - getExtendedPaddingTop() - - getExtendedPaddingBottom(); - } + int boxht = getBoxHeight(l); int textht = l.getHeight(); if (textht < boxht) { @@ -5144,6 +5166,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener voffset = getVerticalOffset(true); } + if (isLayoutModeOptical(mParent)) { + voffset -= getOpticalInsets().top; + } + return getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(0); } @@ -6462,7 +6488,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mDeferScroll = -1; bringPointIntoView(Math.min(curs, mText.length())); } - if (changed && mEditor != null) mEditor.invalidateTextDisplayList(); } private boolean isShowingHint() { @@ -6556,15 +6581,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int line = layout.getLineForOffset(offset); - // FIXME: Is it okay to truncate this, or should we round? - final int x = (int)layout.getPrimaryHorizontal(offset); - final int top = layout.getLineTop(line); - final int bottom = layout.getLineTop(line + 1); - - int left = (int) FloatMath.floor(layout.getLineLeft(line)); - int right = (int) FloatMath.ceil(layout.getLineRight(line)); - int ht = layout.getHeight(); - int grav; switch (layout.getParagraphAlignment(line)) { @@ -6586,8 +6602,32 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; } + // We only want to clamp the cursor to fit within the layout width + // in left-to-right modes, because in a right to left alignment, + // we want to scroll to keep the line-right on the screen, as other + // lines are likely to have text flush with the right margin, which + // we want to keep visible. + // A better long-term solution would probably be to measure both + // the full line and a blank-trimmed version, and, for example, use + // the latter measurement for centering and right alignment, but for + // the time being we only implement the cursor clamping in left to + // right where it is most likely to be annoying. + final boolean clamped = grav > 0; + // FIXME: Is it okay to truncate this, or should we round? + final int x = (int)layout.getPrimaryHorizontal(offset, clamped); + final int top = layout.getLineTop(line); + final int bottom = layout.getLineTop(line + 1); + + int left = (int) FloatMath.floor(layout.getLineLeft(line)); + int right = (int) FloatMath.ceil(layout.getLineRight(line)); + int ht = layout.getHeight(); + int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(); int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom(); + if (!mHorizontallyScrolling && right - left > hspace && right > x) { + // If cursor has been clamped, make sure we don't scroll. + right = Math.max(x, left + hspace); + } int hslack = (bottom - top) / 2; int vslack = hslack; @@ -7154,6 +7194,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ protected void onSelectionChanged(int selStart, int selEnd) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); + notifyAccessibilityStateChanged(); } /** @@ -7730,7 +7771,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the TextView_textColor attribute from the - * Resources.StyledAttributes, if set, or the TextAppearance_textColor + * TypedArray, if set, or the TextAppearance_textColor * from the TextView_textAppearance attribute, if TextView_textColor * was not set directly. */ @@ -7828,27 +7869,46 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener (isTextSelectable() && mText instanceof Spannable && isEnabled()); } + private Locale getTextServicesLocale(boolean allowNullLocale) { + // Start fetching the text services locale asynchronously. + updateTextServicesLocaleAsync(); + // If !allowNullLocale and there is no cached text services locale, just return the default + // locale. + return (mCurrentSpellCheckerLocaleCache == null && !allowNullLocale) ? Locale.getDefault() + : mCurrentSpellCheckerLocaleCache; + } + /** * This is a temporary method. Future versions may support multi-locale text. * Caveat: This method may not return the latest text services locale, but this should be * acceptable and it's more important to make this method asynchronous. * - * @return The locale that should be used for a word iterator and a spell checker + * @return The locale that should be used for a word iterator * in this TextView, based on the current spell checker settings, * the current IME's locale, or the system default locale. + * Please note that a word iterator in this TextView is different from another word iterator + * used by SpellChecker.java of TextView. This method should be used for the former. * @hide */ // TODO: Support multi-locale // TODO: Update the text services locale immediately after the keyboard locale is switched // by catching intent of keyboard switch event public Locale getTextServicesLocale() { - if (mCurrentTextServicesLocaleCache == null) { - // If there is no cached text services locale, just return the default locale. - mCurrentTextServicesLocaleCache = Locale.getDefault(); - } - // Start fetching the text services locale asynchronously. - updateTextServicesLocaleAsync(); - return mCurrentTextServicesLocaleCache; + return getTextServicesLocale(false /* allowNullLocale */); + } + + /** + * This is a temporary method. Future versions may support multi-locale text. + * Caveat: This method may not return the latest spell checker locale, but this should be + * acceptable and it's more important to make this method asynchronous. + * + * @return The locale that should be used for a spell checker in this TextView, + * based on the current spell checker settings, the current IME's locale, or the system default + * locale. + * @hide + */ + public Locale getSpellCheckerLocale() { + return getTextServicesLocale(true /* allowNullLocale */); } private void updateTextServicesLocaleAsync() { @@ -7867,14 +7927,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void updateTextServicesLocaleLocked() { - Locale locale = Locale.getDefault(); final TextServicesManager textServicesManager = (TextServicesManager) mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); final SpellCheckerSubtype subtype = textServicesManager.getCurrentSpellCheckerSubtype(true); + final Locale locale; if (subtype != null) { locale = SpellCheckerSubtype.constructLocaleFromString(subtype.getLocale()); + } else { + locale = null; } - mCurrentTextServicesLocaleCache = locale; + mCurrentSpellCheckerLocaleCache = locale; } void onLocaleChanged() { @@ -7944,6 +8006,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener info.setText(getTextForAccessibility()); } + if (mBufferType == BufferType.EDITABLE) { + info.setEditable(true); + } + if (TextUtils.isEmpty(getContentDescription()) && !TextUtils.isEmpty(mText)) { info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); @@ -7953,6 +8019,82 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE); } + if (isFocused()) { + if (canSelectText()) { + info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION); + } + if (canCopy()) { + info.addAction(AccessibilityNodeInfo.ACTION_COPY); + } + if (canPaste()) { + info.addAction(AccessibilityNodeInfo.ACTION_PASTE); + } + if (canCut()) { + info.addAction(AccessibilityNodeInfo.ACTION_CUT); + } + } + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + switch (action) { + case AccessibilityNodeInfo.ACTION_COPY: { + if (isFocused() && canCopy()) { + if (onTextContextMenuItem(ID_COPY)) { + notifyAccessibilityStateChanged(); + return true; + } + } + } return false; + case AccessibilityNodeInfo.ACTION_PASTE: { + if (isFocused() && canPaste()) { + if (onTextContextMenuItem(ID_PASTE)) { + notifyAccessibilityStateChanged(); + return true; + } + } + } return false; + case AccessibilityNodeInfo.ACTION_CUT: { + if (isFocused() && canCut()) { + if (onTextContextMenuItem(ID_CUT)) { + notifyAccessibilityStateChanged(); + return true; + } + } + } return false; + case AccessibilityNodeInfo.ACTION_SET_SELECTION: { + if (isFocused() && canSelectText()) { + CharSequence text = getIterableTextForAccessibility(); + if (text == null) { + return false; + } + final int start = (arguments != null) ? arguments.getInt( + AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1; + final int end = (arguments != null) ? arguments.getInt( + AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1; + if ((getSelectionStart() != start || getSelectionEnd() != end)) { + // No arguments clears the selection. + if (start == end && end == -1) { + Selection.removeSelection((Spannable) text); + notifyAccessibilityStateChanged(); + return true; + } + if (start >= 0 && start <= end && end <= text.length()) { + Selection.setSelection((Spannable) text, start, end); + // Make sure selection mode is engaged. + if (mEditor != null) { + mEditor.startSelectionActionMode(); + } + notifyAccessibilityStateChanged(); + return true; + } + } + } + } return false; + default: { + return super.performAccessibilityAction(action, arguments); + } + } } @Override @@ -8522,32 +8664,51 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ @Override - public int getAccessibilityCursorPosition() { + public int getAccessibilitySelectionStart() { + if (TextUtils.isEmpty(getContentDescription())) { + final int selectionStart = getSelectionStart(); + if (selectionStart >= 0) { + return selectionStart; + } + } + return ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; + } + + /** + * @hide + */ + public boolean isAccessibilitySelectionExtendable() { + return true; + } + + /** + * @hide + */ + @Override + public int getAccessibilitySelectionEnd() { if (TextUtils.isEmpty(getContentDescription())) { final int selectionEnd = getSelectionEnd(); if (selectionEnd >= 0) { return selectionEnd; } } - return super.getAccessibilityCursorPosition(); + return ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; } /** * @hide */ @Override - public void setAccessibilityCursorPosition(int index) { - if (getAccessibilityCursorPosition() == index) { + public void setAccessibilitySelection(int start, int end) { + if (getAccessibilitySelectionStart() == start + && getAccessibilitySelectionEnd() == end) { return; } - if (TextUtils.isEmpty(getContentDescription())) { - if (index >= 0 && index <= mText.length()) { - Selection.setSelection((Spannable) mText, index); - } else { - Selection.removeSelection((Spannable) mText); - } + CharSequence text = getIterableTextForAccessibility(); + if (start >= 0 && start <= end && end <= text.length()) { + Selection.setSelection((Spannable) text, start, end); } else { - super.setAccessibilityCursorPosition(index); + Selection.removeSelection((Spannable) text); } } @@ -8669,11 +8830,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void drawTextRun(Canvas c, int start, int end, - int contextStart, int contextEnd, float x, float y, int flags, Paint p) { + int contextStart, int contextEnd, float x, float y, Paint p) { int count = end - start; int contextCount = contextEnd - contextStart; c.drawTextRun(mChars, start + mStart, count, contextStart + mStart, - contextCount, x, y, flags, p); + contextCount, x, y, p); } public float measureText(int start, int end, Paint p) { @@ -8685,30 +8846,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public float getTextRunAdvances(int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex, + int contextEnd, float[] advances, int advancesIndex, Paint p) { int count = end - start; int contextCount = contextEnd - contextStart; return p.getTextRunAdvances(mChars, start + mStart, count, - contextStart + mStart, contextCount, flags, advances, + contextStart + mStart, contextCount, advances, advancesIndex); } - public float getTextRunAdvances(int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex, - Paint p, int reserved) { - int count = end - start; - int contextCount = contextEnd - contextStart; - return p.getTextRunAdvances(mChars, start + mStart, count, - contextStart + mStart, contextCount, flags, advances, - advancesIndex, reserved); - } - - public int getTextRunCursor(int contextStart, int contextEnd, int flags, + public int getTextRunCursor(int contextStart, int contextEnd, int offset, int cursorOpt, Paint p) { int contextCount = contextEnd - contextStart; return p.getTextRunCursor(mChars, contextStart + mStart, - contextCount, flags, offset + mStart, cursorOpt); + contextCount, offset + mStart, cursorOpt); } } diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java new file mode 100755 index 0000000..2b91abf --- /dev/null +++ b/core/java/android/widget/ValueEditor.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.util.ValueModel; + +/** + * An interface for editors of simple values. Classes implementing this interface are normally + * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable + * user interface to display and edit values of the specified type. This interface is + * intended to describe editors for simple types, like {@code boolean}, {@code int} or + * {@code String}, where the values themselves are immutable. + * <p> + * For example, {@link android.widget.CheckBox CheckBox} implements + * this interface for the Boolean type as it is capable of providing an appropriate + * mechanism for displaying and changing the value of a Boolean property. + * + * @param <T> the value type that this editor supports + */ +public interface ValueEditor<T> { + /** + * Return the last value model that was set. If no value model has been set, the editor + * should return the value {@link android.util.ValueModel#EMPTY}. + * + * @return the value model + */ + public ValueModel<T> getValueModel(); + + /** + * Sets the value model for this editor. When the value model is set, the editor should + * retrieve the value from the value model, using {@link android.util.ValueModel#get()}, + * and set its internal state accordingly. Likewise, when the editor's internal state changes + * it should update the value model by calling {@link android.util.ValueModel#set(T)} + * with the appropriate value. + * + * @param valueModel the new value model for this editor. + */ + public void setValueModel(ValueModel<T> valueModel); +} diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 329b0df..16b6a76 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -35,6 +35,7 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; +import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.MediaController.MediaPlayerControl; @@ -107,23 +108,65 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - //Log.i("@@@@", "onMeasure"); + //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " + // + MeasureSpec.toString(heightMeasureSpec) + ")"); + int width = getDefaultSize(mVideoWidth, widthMeasureSpec); int height = getDefaultSize(mVideoHeight, heightMeasureSpec); if (mVideoWidth > 0 && mVideoHeight > 0) { - if ( mVideoWidth * height > width * mVideoHeight ) { - //Log.i("@@@", "image too tall, correcting"); + + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { + // the size is fixed + width = widthSpecSize; + height = heightSpecSize; + + // for compatibility, we adjust size based on aspect ratio + if ( mVideoWidth * height < width * mVideoHeight ) { + //Log.i("@@@", "image too wide, correcting"); + width = height * mVideoWidth / mVideoHeight; + } else if ( mVideoWidth * height > width * mVideoHeight ) { + //Log.i("@@@", "image too tall, correcting"); + height = width * mVideoHeight / mVideoWidth; + } + } else if (widthSpecMode == MeasureSpec.EXACTLY) { + // only the width is fixed, adjust the height to match aspect ratio if possible + width = widthSpecSize; height = width * mVideoHeight / mVideoWidth; - } else if ( mVideoWidth * height < width * mVideoHeight ) { - //Log.i("@@@", "image too wide, correcting"); + if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { + // couldn't match aspect ratio within the constraints + height = heightSpecSize; + } + } else if (heightSpecMode == MeasureSpec.EXACTLY) { + // only the height is fixed, adjust the width to match aspect ratio if possible + height = heightSpecSize; width = height * mVideoWidth / mVideoHeight; + if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { + // couldn't match aspect ratio within the constraints + width = widthSpecSize; + } } else { - //Log.i("@@@", "aspect ratio is correct: " + - //width+"/"+height+"="+ - //mVideoWidth+"/"+mVideoHeight); + // neither the width nor the height are fixed, try to use actual video size + width = mVideoWidth; + height = mVideoHeight; + if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { + // too tall, decrease both width and height + height = heightSpecSize; + width = height * mVideoWidth / mVideoHeight; + } + if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { + // too wide, decrease both width and height + width = widthSpecSize; + height = width * mVideoHeight / mVideoWidth; + } } + } else { + // no size yet, just adopt the given spec sizes } - //Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height); setMeasuredDimension(width, height); } @@ -140,33 +183,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { } public int resolveAdjustedSize(int desiredSize, int measureSpec) { - int result = desiredSize; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); - - switch (specMode) { - case MeasureSpec.UNSPECIFIED: - /* Parent says we can be as big as we want. Just don't be larger - * than max size imposed on ourselves. - */ - result = desiredSize; - break; - - case MeasureSpec.AT_MOST: - /* Parent says we can be as big as we want, up to specSize. - * Don't be larger than specSize, and don't be larger than - * the max size imposed on ourselves. - */ - result = Math.min(desiredSize, specSize); - break; - - case MeasureSpec.EXACTLY: - // No choice. Do what we are told. - result = specSize; - break; - } - return result; -} + return getDefaultSize(desiredSize, measureSpec); + } private void initVideoView() { mVideoWidth = 0; diff --git a/core/java/android/util/Poolable.java b/core/java/com/android/internal/app/IAppOpsCallback.aidl index 87e0529..afbc609 100644 --- a/core/java/android/util/Poolable.java +++ b/core/java/com/android/internal/app/IAppOpsCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,10 @@ * limitations under the License. */ -package android.util; +package com.android.internal.app; -/** - * @hide - */ -public interface Poolable<T> { - void setNextPoolable(T element); - T getNextPoolable(); - boolean isPooled(); - void setPooled(boolean isPooled); +// This interface is also used by native code, so must +// be kept in sync with frameworks/native/include/binder/IAppOpsCallback.h +oneway interface IAppOpsCallback { + void opChanged(int op, String packageName); } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl new file mode 100644 index 0000000..a9da863 --- /dev/null +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AppOpsManager; +import com.android.internal.app.IAppOpsCallback; + +interface IAppOpsService { + // These first methods are also called by native code, so must + // be kept in sync with frameworks/native/include/binder/IAppOpsService.h + int checkOperation(int code, int uid, String packageName); + int noteOperation(int code, int uid, String packageName); + int startOperation(int code, int uid, String packageName); + void finishOperation(int code, int uid, String packageName); + void startWatchingMode(int op, String packageName, IAppOpsCallback callback); + void stopWatchingMode(IAppOpsCallback callback); + + // Remaining methods are only used in Java. + List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops); + List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); + void setMode(int code, int uid, String packageName, int mode); +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 1a76461f..823e19f 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -37,6 +37,8 @@ interface IBatteryStats { void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type); void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type); + void noteVibratorOn(int uid, long durationMillis); + void noteVibratorOff(int uid); void noteStartGps(int uid); void noteStopGps(int uid); void noteScreenOn(); diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl index 78b4466..6d51d38 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl @@ -22,9 +22,9 @@ import android.widget.RemoteViews; /** {@hide} */ oneway interface IAppWidgetHost { - void updateAppWidget(int appWidgetId, in RemoteViews views); - void providerChanged(int appWidgetId, in AppWidgetProviderInfo info); - void providersChanged(); - void viewDataChanged(int appWidgetId, int viewId); + void updateAppWidget(int appWidgetId, in RemoteViews views, int userId); + void providerChanged(int appWidgetId, in AppWidgetProviderInfo info, int userId); + void providersChanged(int userId); + void viewDataChanged(int appWidgetId, int viewId, int userId); } diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index e685e63..7ddd5d2 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -26,42 +26,39 @@ import android.widget.RemoteViews; /** {@hide} */ interface IAppWidgetService { - + // // for AppWidgetHost // int[] startListening(IAppWidgetHost host, String packageName, int hostId, - out List<RemoteViews> updatedViews); - int[] startListeningAsUser(IAppWidgetHost host, String packageName, int hostId, out List<RemoteViews> updatedViews, int userId); - void stopListening(int hostId); - void stopListeningAsUser(int hostId, int userId); - int allocateAppWidgetId(String packageName, int hostId); - void deleteAppWidgetId(int appWidgetId); - void deleteHost(int hostId); - void deleteAllHosts(); - RemoteViews getAppWidgetViews(int appWidgetId); - int[] getAppWidgetIdsForHost(int hostId); + void stopListening(int hostId, int userId); + int allocateAppWidgetId(String packageName, int hostId, int userId); + void deleteAppWidgetId(int appWidgetId, int userId); + void deleteHost(int hostId, int userId); + void deleteAllHosts(int userId); + RemoteViews getAppWidgetViews(int appWidgetId, int userId); + int[] getAppWidgetIdsForHost(int hostId, int userId); // // for AppWidgetManager // - void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views); - void updateAppWidgetOptions(int appWidgetId, in Bundle extras); - Bundle getAppWidgetOptions(int appWidgetId); - void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views); - void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views); - void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId); - List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter); - AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId); - boolean hasBindAppWidgetPermission(in String packageName); - void setBindAppWidgetPermission(in String packageName, in boolean permission); - void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options); - boolean bindAppWidgetIdIfAllowed( - in String packageName, int appWidgetId, in ComponentName provider, in Bundle options); + void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId); + void updateAppWidgetOptions(int appWidgetId, in Bundle extras, int userId); + Bundle getAppWidgetOptions(int appWidgetId, int userId); + void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId); + void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views, int userId); + void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId, int userId); + List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId); + AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId); + boolean hasBindAppWidgetPermission(in String packageName, int userId); + void setBindAppWidgetPermission(in String packageName, in boolean permission, int userId); + void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options, int userId); + boolean bindAppWidgetIdIfAllowed(in String packageName, int appWidgetId, + in ComponentName provider, in Bundle options, int userId); void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId); void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId); - int[] getAppWidgetIds(in ComponentName provider); + int[] getAppWidgetIds(in ComponentName provider, int userId); } diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 20ecace..424c19b 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -153,8 +153,33 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { public void onPackageUpdateFinished(String packageName, int uid) { } - - public void onPackageChanged(String packageName, int uid, String[] components) { + + /** + * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED + * Intent.ACTION_PACKAGE_CHANGED} being received, informing you of + * changes to the enabled/disabled state of components in a package + * and/or of the overall package. + * + * @param packageName The name of the package that is changing. + * @param uid The user ID the package runs under. + * @param components Any components in the package that are changing. If + * the overall package is changing, this will contain an entry of the + * package name itself. + * @return Return true to indicate you care about this change, which will + * result in {@link #onSomePackagesChanged()} being called later. If you + * return false, no further callbacks will happen about this change. The + * default implementation returns true if this is a change to the entire + * package. + */ + public boolean onPackageChanged(String packageName, int uid, String[] components) { + if (components != null) { + for (String name : components) { + if (packageName.equals(name)) { + return true; + } + } + } + return false; } public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { @@ -189,7 +214,10 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { */ public void onPackageAppeared(String packageName, int reason) { } - + + /** + * Called when an existing package is updated or its disabled state changes. + */ public void onPackageModified(String packageName) { } @@ -328,9 +356,10 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { if (pkg != null) { mModifiedPackages = mTempArray; mTempArray[0] = pkg; - onPackageChanged(pkg, uid, components); - // XXX Don't want this to always cause mSomePackagesChanged, - // since it can happen a fair amount. + mChangeType = PACKAGE_PERMANENT_CHANGE; + if (onPackageChanged(pkg, uid, components)) { + mSomePackagesChanged = true; + } onPackageModified(pkg); } } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java new file mode 100644 index 0000000..655d148 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; +import android.util.Pair; +import android.util.Slog; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +/** + * InputMethodManagerUtils contains some static methods that provides IME informations. + * This methods are supposed to be used in both the framework and the Settings application. + */ +public class InputMethodUtils { + public static final boolean DEBUG = false; + public static final int NOT_A_SUBTYPE_ID = -1; + public static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; + public static final String SUBTYPE_MODE_VOICE = "voice"; + private static final String TAG = "InputMethodUtils"; + private static final Locale ENGLISH_LOCALE = new Locale("en"); + private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID); + private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = + "EnabledWhenDefaultIsNotAsciiCapable"; + private static final String TAG_ASCII_CAPABLE = "AsciiCapable"; + + private InputMethodUtils() { + // This utility class is not publicly instantiable. + } + + public static boolean isSystemIme(InputMethodInfo inputMethod) { + return (inputMethod.getServiceInfo().applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) { + if (!isSystemIme(imi)) { + return false; + } + return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD); + } + + private static boolean isSystemAuxilialyImeThatHashAutomaticSubtype(InputMethodInfo imi) { + if (!isSystemIme(imi)) { + return false; + } + if (!imi.isAuxiliaryIme()) { + return false; + } + final int subtypeCount = imi.getSubtypeCount(); + for (int i = 0; i < subtypeCount; ++i) { + final InputMethodSubtype s = imi.getSubtypeAt(i); + if (s.overridesImplicitlyEnabledSubtype()) { + return true; + } + } + return false; + } + + public static ArrayList<InputMethodInfo> getDefaultEnabledImes( + Context context, boolean isSystemReady, ArrayList<InputMethodInfo> imis) { + final ArrayList<InputMethodInfo> retval = new ArrayList<InputMethodInfo>(); + boolean auxilialyImeAdded = false; + for (int i = 0; i < imis.size(); ++i) { + final InputMethodInfo imi = imis.get(i); + if (isDefaultEnabledIme(isSystemReady, imi, context)) { + retval.add(imi); + if (imi.isAuxiliaryIme()) { + auxilialyImeAdded = true; + } + } + } + if (auxilialyImeAdded) { + return retval; + } + for (int i = 0; i < imis.size(); ++i) { + final InputMethodInfo imi = imis.get(i); + if (isSystemAuxilialyImeThatHashAutomaticSubtype(imi)) { + retval.add(imi); + } + } + return retval; + } + + // TODO: Rename isSystemDefaultImeThatHasCurrentLanguageSubtype + public static boolean isValidSystemDefaultIme( + boolean isSystemReady, InputMethodInfo imi, Context context) { + if (!isSystemReady) { + return false; + } + if (!isSystemIme(imi)) { + return false; + } + if (imi.getIsDefaultResourceId() != 0) { + try { + if (imi.isDefault(context) && containsSubtypeOf( + imi, context.getResources().getConfiguration().locale.getLanguage(), + null /* mode */)) { + return true; + } + } catch (Resources.NotFoundException ex) { + } + } + if (imi.getSubtypeCount() == 0) { + Slog.w(TAG, "Found no subtypes in a system IME: " + imi.getPackageName()); + } + return false; + } + + public static boolean isDefaultEnabledIme( + boolean isSystemReady, InputMethodInfo imi, Context context) { + return isValidSystemDefaultIme(isSystemReady, imi, context) + || isSystemImeThatHasEnglishKeyboardSubtype(imi); + } + + private static boolean containsSubtypeOf(InputMethodInfo imi, String language, String mode) { + final int N = imi.getSubtypeCount(); + for (int i = 0; i < N; ++i) { + if (!imi.getSubtypeAt(i).getLocale().startsWith(language)) { + continue; + } + if(!TextUtils.isEmpty(mode) && !imi.getSubtypeAt(i).getMode().equalsIgnoreCase(mode)) { + continue; + } + return true; + } + return false; + } + + public static ArrayList<InputMethodSubtype> getSubtypes(InputMethodInfo imi) { + ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + final int subtypeCount = imi.getSubtypeCount(); + for (int i = 0; i < subtypeCount; ++i) { + subtypes.add(imi.getSubtypeAt(i)); + } + return subtypes; + } + + public static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes( + InputMethodInfo imi, String mode) { + ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + final int subtypeCount = imi.getSubtypeCount(); + for (int i = 0; i < subtypeCount; ++i) { + final InputMethodSubtype subtype = imi.getSubtypeAt(i); + if (subtype.overridesImplicitlyEnabledSubtype() && subtype.getMode().equals(mode)) { + subtypes.add(subtype); + } + } + return subtypes; + } + + public static InputMethodInfo getMostApplicableDefaultIME( + List<InputMethodInfo> enabledImes) { + if (enabledImes != null && enabledImes.size() > 0) { + // We'd prefer to fall back on a system IME, since that is safer. + int i = enabledImes.size(); + int firstFoundSystemIme = -1; + while (i > 0) { + i--; + final InputMethodInfo imi = enabledImes.get(i); + if (InputMethodUtils.isSystemImeThatHasEnglishKeyboardSubtype(imi) + && !imi.isAuxiliaryIme()) { + return imi; + } + if (firstFoundSystemIme < 0 && InputMethodUtils.isSystemIme(imi) + && !imi.isAuxiliaryIme()) { + firstFoundSystemIme = i; + } + } + return enabledImes.get(Math.max(firstFoundSystemIme, 0)); + } + return null; + } + + public static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { + return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID; + } + + public static int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) { + if (imi != null) { + final int subtypeCount = imi.getSubtypeCount(); + for (int i = 0; i < subtypeCount; ++i) { + InputMethodSubtype ims = imi.getSubtypeAt(i); + if (subtypeHashCode == ims.hashCode()) { + return i; + } + } + } + return NOT_A_SUBTYPE_ID; + } + + private static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked( + Resources res, InputMethodInfo imi) { + final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi); + final String systemLocale = res.getConfiguration().locale.toString(); + if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>(); + final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = + new HashMap<String, InputMethodSubtype>(); + final int N = subtypes.size(); + for (int i = 0; i < N; ++i) { + // scan overriding implicitly enabled subtypes. + InputMethodSubtype subtype = subtypes.get(i); + if (subtype.overridesImplicitlyEnabledSubtype()) { + final String mode = subtype.getMode(); + if (!applicableModeAndSubtypesMap.containsKey(mode)) { + applicableModeAndSubtypesMap.put(mode, subtype); + } + } + } + if (applicableModeAndSubtypesMap.size() > 0) { + return new ArrayList<InputMethodSubtype>(applicableModeAndSubtypesMap.values()); + } + for (int i = 0; i < N; ++i) { + final InputMethodSubtype subtype = subtypes.get(i); + final String locale = subtype.getLocale(); + final String mode = subtype.getMode(); + // When system locale starts with subtype's locale, that subtype will be applicable + // for system locale + // For instance, it's clearly applicable for cases like system locale = en_US and + // subtype = en, but it is not necessarily considered applicable for cases like system + // locale = en and subtype = en_US. + // We just call systemLocale.startsWith(locale) in this function because there is no + // need to find applicable subtypes aggressively unlike + // findLastResortApplicableSubtypeLocked. + if (systemLocale.startsWith(locale)) { + final InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode); + // If more applicable subtypes are contained, skip. + if (applicableSubtype != null) { + if (systemLocale.equals(applicableSubtype.getLocale())) continue; + if (!systemLocale.equals(locale)) continue; + } + applicableModeAndSubtypesMap.put(mode, subtype); + } + } + final InputMethodSubtype keyboardSubtype + = applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD); + final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( + applicableModeAndSubtypesMap.values()); + if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) { + for (int i = 0; i < N; ++i) { + final InputMethodSubtype subtype = subtypes.get(i); + final String mode = subtype.getMode(); + if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey( + TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) { + applicableSubtypes.add(subtype); + } + } + } + if (keyboardSubtype == null) { + InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked( + res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true); + if (lastResortKeyboardSubtype != null) { + applicableSubtypes.add(lastResortKeyboardSubtype); + } + } + return applicableSubtypes; + } + + private static List<InputMethodSubtype> getEnabledInputMethodSubtypeList( + Context context, InputMethodInfo imi, List<InputMethodSubtype> enabledSubtypes, + boolean allowsImplicitlySelectedSubtypes) { + if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) { + enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( + context.getResources(), imi); + } + return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes); + } + + /** + * If there are no selected subtypes, tries finding the most applicable one according to the + * given locale. + * @param subtypes this function will search the most applicable subtype in subtypes + * @param mode subtypes will be filtered by mode + * @param locale subtypes will be filtered by locale + * @param canIgnoreLocaleAsLastResort if this function can't find the most applicable subtype, + * it will return the first subtype matched with mode + * @return the most applicable subtypeId + */ + public static InputMethodSubtype findLastResortApplicableSubtypeLocked( + Resources res, List<InputMethodSubtype> subtypes, String mode, String locale, + boolean canIgnoreLocaleAsLastResort) { + if (subtypes == null || subtypes.size() == 0) { + return null; + } + if (TextUtils.isEmpty(locale)) { + locale = res.getConfiguration().locale.toString(); + } + final String language = locale.substring(0, 2); + boolean partialMatchFound = false; + InputMethodSubtype applicableSubtype = null; + InputMethodSubtype firstMatchedModeSubtype = null; + final int N = subtypes.size(); + for (int i = 0; i < N; ++i) { + InputMethodSubtype subtype = subtypes.get(i); + final String subtypeLocale = subtype.getLocale(); + // An applicable subtype should match "mode". If mode is null, mode will be ignored, + // and all subtypes with all modes can be candidates. + if (mode == null || subtypes.get(i).getMode().equalsIgnoreCase(mode)) { + if (firstMatchedModeSubtype == null) { + firstMatchedModeSubtype = subtype; + } + if (locale.equals(subtypeLocale)) { + // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US") + applicableSubtype = subtype; + break; + } else if (!partialMatchFound && subtypeLocale.startsWith(language)) { + // Partial match (e.g. system locale is "en_US" and subtype locale is "en") + applicableSubtype = subtype; + partialMatchFound = true; + } + } + } + + if (applicableSubtype == null && canIgnoreLocaleAsLastResort) { + return firstMatchedModeSubtype; + } + + // The first subtype applicable to the system locale will be defined as the most applicable + // subtype. + if (DEBUG) { + if (applicableSubtype != null) { + Slog.d(TAG, "Applicable InputMethodSubtype was found: " + + applicableSubtype.getMode() + "," + applicableSubtype.getLocale()); + } + } + return applicableSubtype; + } + + public static boolean canAddToLastInputMethod(InputMethodSubtype subtype) { + if (subtype == null) return true; + return !subtype.isAuxiliary(); + } + + /** + * Utility class for putting and getting settings for InputMethod + * TODO: Move all putters and getters of settings to this class. + */ + public static class InputMethodSettings { + // The string for enabled input method is saved as follows: + // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0") + private static final char INPUT_METHOD_SEPARATER = ':'; + private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';'; + private final TextUtils.SimpleStringSplitter mInputMethodSplitter = + new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER); + + private final TextUtils.SimpleStringSplitter mSubtypeSplitter = + new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER); + + private final Resources mRes; + private final ContentResolver mResolver; + private final HashMap<String, InputMethodInfo> mMethodMap; + private final ArrayList<InputMethodInfo> mMethodList; + + private String mEnabledInputMethodsStrCache; + private int mCurrentUserId; + + private static void buildEnabledInputMethodsSettingString( + StringBuilder builder, Pair<String, ArrayList<String>> pair) { + String id = pair.first; + ArrayList<String> subtypes = pair.second; + builder.append(id); + // Inputmethod and subtypes are saved in the settings as follows: + // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1 + for (String subtypeId: subtypes) { + builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId); + } + } + + public InputMethodSettings( + Resources res, ContentResolver resolver, + HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList, + int userId) { + setCurrentUserId(userId); + mRes = res; + mResolver = resolver; + mMethodMap = methodMap; + mMethodList = methodList; + } + + public void setCurrentUserId(int userId) { + if (DEBUG) { + Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " + + userId + ", new ime = " + getSelectedInputMethod()); + } + // IMMS settings are kept per user, so keep track of current user + mCurrentUserId = userId; + } + + public List<InputMethodInfo> getEnabledInputMethodListLocked() { + return createEnabledInputMethodListLocked( + getEnabledInputMethodsAndSubtypeListLocked()); + } + + public List<Pair<InputMethodInfo, ArrayList<String>>> + getEnabledInputMethodAndSubtypeHashCodeListLocked() { + return createEnabledInputMethodAndSubtypeHashCodeListLocked( + getEnabledInputMethodsAndSubtypeListLocked()); + } + + public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked( + Context context, InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) { + List<InputMethodSubtype> enabledSubtypes = + getEnabledInputMethodSubtypeListLocked(imi); + if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) { + enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( + context.getResources(), imi); + } + return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes); + } + + private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked( + InputMethodInfo imi) { + List<Pair<String, ArrayList<String>>> imsList = + getEnabledInputMethodsAndSubtypeListLocked(); + ArrayList<InputMethodSubtype> enabledSubtypes = + new ArrayList<InputMethodSubtype>(); + if (imi != null) { + for (Pair<String, ArrayList<String>> imsPair : imsList) { + InputMethodInfo info = mMethodMap.get(imsPair.first); + if (info != null && info.getId().equals(imi.getId())) { + final int subtypeCount = info.getSubtypeCount(); + for (int i = 0; i < subtypeCount; ++i) { + InputMethodSubtype ims = info.getSubtypeAt(i); + for (String s: imsPair.second) { + if (String.valueOf(ims.hashCode()).equals(s)) { + enabledSubtypes.add(ims); + } + } + } + break; + } + } + } + return enabledSubtypes; + } + + // At the initial boot, the settings for input methods are not set, + // so we need to enable IME in that case. + public void enableAllIMEsIfThereIsNoEnabledIME() { + if (TextUtils.isEmpty(getEnabledInputMethodsStr())) { + StringBuilder sb = new StringBuilder(); + final int N = mMethodList.size(); + for (int i = 0; i < N; i++) { + InputMethodInfo imi = mMethodList.get(i); + Slog.i(TAG, "Adding: " + imi.getId()); + if (i > 0) sb.append(':'); + sb.append(imi.getId()); + } + putEnabledInputMethodsStr(sb.toString()); + } + } + + public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() { + ArrayList<Pair<String, ArrayList<String>>> imsList + = new ArrayList<Pair<String, ArrayList<String>>>(); + final String enabledInputMethodsStr = getEnabledInputMethodsStr(); + if (TextUtils.isEmpty(enabledInputMethodsStr)) { + return imsList; + } + mInputMethodSplitter.setString(enabledInputMethodsStr); + while (mInputMethodSplitter.hasNext()) { + String nextImsStr = mInputMethodSplitter.next(); + mSubtypeSplitter.setString(nextImsStr); + if (mSubtypeSplitter.hasNext()) { + ArrayList<String> subtypeHashes = new ArrayList<String>(); + // The first element is ime id. + String imeId = mSubtypeSplitter.next(); + while (mSubtypeSplitter.hasNext()) { + subtypeHashes.add(mSubtypeSplitter.next()); + } + imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes)); + } + } + return imsList; + } + + public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) { + if (reloadInputMethodStr) { + getEnabledInputMethodsStr(); + } + if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) { + // Add in the newly enabled input method. + putEnabledInputMethodsStr(id); + } else { + putEnabledInputMethodsStr( + mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATER + id); + } + } + + /** + * Build and put a string of EnabledInputMethods with removing specified Id. + * @return the specified id was removed or not. + */ + public boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked( + StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) { + boolean isRemoved = false; + boolean needsAppendSeparator = false; + for (Pair<String, ArrayList<String>> ims: imsList) { + String curId = ims.first; + if (curId.equals(id)) { + // We are disabling this input method, and it is + // currently enabled. Skip it to remove from the + // new list. + isRemoved = true; + } else { + if (needsAppendSeparator) { + builder.append(INPUT_METHOD_SEPARATER); + } else { + needsAppendSeparator = true; + } + buildEnabledInputMethodsSettingString(builder, ims); + } + } + if (isRemoved) { + // Update the setting with the new list of input methods. + putEnabledInputMethodsStr(builder.toString()); + } + return isRemoved; + } + + private List<InputMethodInfo> createEnabledInputMethodListLocked( + List<Pair<String, ArrayList<String>>> imsList) { + final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>(); + for (Pair<String, ArrayList<String>> ims: imsList) { + InputMethodInfo info = mMethodMap.get(ims.first); + if (info != null) { + res.add(info); + } + } + return res; + } + + private List<Pair<InputMethodInfo, ArrayList<String>>> + createEnabledInputMethodAndSubtypeHashCodeListLocked( + List<Pair<String, ArrayList<String>>> imsList) { + final ArrayList<Pair<InputMethodInfo, ArrayList<String>>> res + = new ArrayList<Pair<InputMethodInfo, ArrayList<String>>>(); + for (Pair<String, ArrayList<String>> ims : imsList) { + InputMethodInfo info = mMethodMap.get(ims.first); + if (info != null) { + res.add(new Pair<InputMethodInfo, ArrayList<String>>(info, ims.second)); + } + } + return res; + } + + private void putEnabledInputMethodsStr(String str) { + Settings.Secure.putStringForUser( + mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str, mCurrentUserId); + mEnabledInputMethodsStrCache = str; + if (DEBUG) { + Slog.d(TAG, "putEnabledInputMethodStr: " + str); + } + } + + public String getEnabledInputMethodsStr() { + mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser( + mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId); + if (DEBUG) { + Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache + + ", " + mCurrentUserId); + } + return mEnabledInputMethodsStrCache; + } + + private void saveSubtypeHistory( + List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) { + StringBuilder builder = new StringBuilder(); + boolean isImeAdded = false; + if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) { + builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append( + newSubtypeId); + isImeAdded = true; + } + for (Pair<String, String> ime: savedImes) { + String imeId = ime.first; + String subtypeId = ime.second; + if (TextUtils.isEmpty(subtypeId)) { + subtypeId = NOT_A_SUBTYPE_ID_STR; + } + if (isImeAdded) { + builder.append(INPUT_METHOD_SEPARATER); + } else { + isImeAdded = true; + } + builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append( + subtypeId); + } + // Remove the last INPUT_METHOD_SEPARATER + putSubtypeHistoryStr(builder.toString()); + } + + private void addSubtypeToHistory(String imeId, String subtypeId) { + List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked(); + for (Pair<String, String> ime: subtypeHistory) { + if (ime.first.equals(imeId)) { + if (DEBUG) { + Slog.v(TAG, "Subtype found in the history: " + imeId + ", " + + ime.second); + } + // We should break here + subtypeHistory.remove(ime); + break; + } + } + if (DEBUG) { + Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId); + } + saveSubtypeHistory(subtypeHistory, imeId, subtypeId); + } + + private void putSubtypeHistoryStr(String str) { + if (DEBUG) { + Slog.d(TAG, "putSubtypeHistoryStr: " + str); + } + Settings.Secure.putStringForUser( + mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str, mCurrentUserId); + } + + public Pair<String, String> getLastInputMethodAndSubtypeLocked() { + // Gets the first one from the history + return getLastSubtypeForInputMethodLockedInternal(null); + } + + public String getLastSubtypeForInputMethodLocked(String imeId) { + Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId); + if (ime != null) { + return ime.second; + } else { + return null; + } + } + + private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) { + List<Pair<String, ArrayList<String>>> enabledImes = + getEnabledInputMethodsAndSubtypeListLocked(); + List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked(); + for (Pair<String, String> imeAndSubtype : subtypeHistory) { + final String imeInTheHistory = imeAndSubtype.first; + // If imeId is empty, returns the first IME and subtype in the history + if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) { + final String subtypeInTheHistory = imeAndSubtype.second; + final String subtypeHashCode = + getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked( + enabledImes, imeInTheHistory, subtypeInTheHistory); + if (!TextUtils.isEmpty(subtypeHashCode)) { + if (DEBUG) { + Slog.d(TAG, "Enabled subtype found in the history: " + subtypeHashCode); + } + return new Pair<String, String>(imeInTheHistory, subtypeHashCode); + } + } + } + if (DEBUG) { + Slog.d(TAG, "No enabled IME found in the history"); + } + return null; + } + + private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String, + ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) { + for (Pair<String, ArrayList<String>> enabledIme: enabledImes) { + if (enabledIme.first.equals(imeId)) { + final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second; + final InputMethodInfo imi = mMethodMap.get(imeId); + if (explicitlyEnabledSubtypes.size() == 0) { + // If there are no explicitly enabled subtypes, applicable subtypes are + // enabled implicitly. + // If IME is enabled and no subtypes are enabled, applicable subtypes + // are enabled implicitly, so needs to treat them to be enabled. + if (imi != null && imi.getSubtypeCount() > 0) { + List<InputMethodSubtype> implicitlySelectedSubtypes = + getImplicitlyApplicableSubtypesLocked(mRes, imi); + if (implicitlySelectedSubtypes != null) { + final int N = implicitlySelectedSubtypes.size(); + for (int i = 0; i < N; ++i) { + final InputMethodSubtype st = implicitlySelectedSubtypes.get(i); + if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) { + return subtypeHashCode; + } + } + } + } + } else { + for (String s: explicitlyEnabledSubtypes) { + if (s.equals(subtypeHashCode)) { + // If both imeId and subtypeId are enabled, return subtypeId. + try { + final int hashCode = Integer.valueOf(subtypeHashCode); + // Check whether the subtype id is valid or not + if (isValidSubtypeId(imi, hashCode)) { + return s; + } else { + return NOT_A_SUBTYPE_ID_STR; + } + } catch (NumberFormatException e) { + return NOT_A_SUBTYPE_ID_STR; + } + } + } + } + // If imeId was enabled but subtypeId was disabled. + return NOT_A_SUBTYPE_ID_STR; + } + } + // If both imeId and subtypeId are disabled, return null + return null; + } + + private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() { + ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>(); + final String subtypeHistoryStr = getSubtypeHistoryStr(); + if (TextUtils.isEmpty(subtypeHistoryStr)) { + return imsList; + } + mInputMethodSplitter.setString(subtypeHistoryStr); + while (mInputMethodSplitter.hasNext()) { + String nextImsStr = mInputMethodSplitter.next(); + mSubtypeSplitter.setString(nextImsStr); + if (mSubtypeSplitter.hasNext()) { + String subtypeId = NOT_A_SUBTYPE_ID_STR; + // The first element is ime id. + String imeId = mSubtypeSplitter.next(); + while (mSubtypeSplitter.hasNext()) { + subtypeId = mSubtypeSplitter.next(); + break; + } + imsList.add(new Pair<String, String>(imeId, subtypeId)); + } + } + return imsList; + } + + private String getSubtypeHistoryStr() { + if (DEBUG) { + Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getStringForUser( + mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId)); + } + return Settings.Secure.getStringForUser( + mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId); + } + + public void putSelectedInputMethod(String imeId) { + if (DEBUG) { + Slog.d(TAG, "putSelectedInputMethodStr: " + imeId + ", " + + mCurrentUserId); + } + Settings.Secure.putStringForUser( + mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId, mCurrentUserId); + } + + public void putSelectedSubtype(int subtypeId) { + if (DEBUG) { + Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", " + + mCurrentUserId); + } + Settings.Secure.putIntForUser(mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, + subtypeId, mCurrentUserId); + } + + public String getDisabledSystemInputMethods() { + return Settings.Secure.getStringForUser( + mResolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, mCurrentUserId); + } + + public String getSelectedInputMethod() { + if (DEBUG) { + Slog.d(TAG, "getSelectedInputMethodStr: " + Settings.Secure.getStringForUser( + mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId) + + ", " + mCurrentUserId); + } + return Settings.Secure.getStringForUser( + mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId); + } + + public boolean isSubtypeSelected() { + return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID; + } + + private int getSelectedInputMethodSubtypeHashCode() { + try { + return Settings.Secure.getIntForUser( + mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, mCurrentUserId); + } catch (SettingNotFoundException e) { + return NOT_A_SUBTYPE_ID; + } + } + + public int getCurrentUserId() { + return mCurrentUserId; + } + + public int getSelectedInputMethodSubtypeId(String selectedImiId) { + final InputMethodInfo imi = mMethodMap.get(selectedImiId); + if (imi == null) { + return NOT_A_SUBTYPE_ID; + } + final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); + return getSubtypeIdFromHashCode(imi, subtypeHashCode); + } + + public void saveCurrentInputMethodAndSubtypeToHistory( + String curMethodId, InputMethodSubtype currentSubtype) { + String subtypeId = NOT_A_SUBTYPE_ID_STR; + if (currentSubtype != null) { + subtypeId = String.valueOf(currentSubtype.hashCode()); + } + if (canAddToLastInputMethod(currentSubtype)) { + addSubtypeToHistory(curMethodId, subtypeId); + } + } + } +} diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java index c517a68..8282d23 100644 --- a/core/java/com/android/internal/net/NetworkStatsFactory.java +++ b/core/java/com/android/internal/net/NetworkStatsFactory.java @@ -31,6 +31,7 @@ import com.android.internal.util.ProcFileReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.net.ProtocolException; import libcore.io.IoUtils; @@ -41,7 +42,8 @@ import libcore.io.IoUtils; public class NetworkStatsFactory { private static final String TAG = "NetworkStatsFactory"; - // TODO: consider moving parsing to native code + private static final boolean USE_NATIVE_PARSING = true; + private static final boolean SANITY_CHECK_NATIVE = false; /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */ private final File mStatsXtIfaceAll; @@ -69,7 +71,7 @@ public class NetworkStatsFactory { * * @throws IllegalStateException when problem parsing stats. */ - public NetworkStats readNetworkStatsSummaryDev() throws IllegalStateException { + public NetworkStats readNetworkStatsSummaryDev() throws IOException { final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); @@ -105,11 +107,9 @@ public class NetworkStatsFactory { reader.finishLine(); } } catch (NullPointerException e) { - throw new IllegalStateException("problem parsing stats: " + e); + throw new ProtocolException("problem parsing stats", e); } catch (NumberFormatException e) { - throw new IllegalStateException("problem parsing stats: " + e); - } catch (IOException e) { - throw new IllegalStateException("problem parsing stats: " + e); + throw new ProtocolException("problem parsing stats", e); } finally { IoUtils.closeQuietly(reader); StrictMode.setThreadPolicy(savedPolicy); @@ -124,7 +124,7 @@ public class NetworkStatsFactory { * * @throws IllegalStateException when problem parsing stats. */ - public NetworkStats readNetworkStatsSummaryXt() throws IllegalStateException { + public NetworkStats readNetworkStatsSummaryXt() throws IOException { final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); // return null when kernel doesn't support @@ -154,11 +154,9 @@ public class NetworkStatsFactory { reader.finishLine(); } } catch (NullPointerException e) { - throw new IllegalStateException("problem parsing stats: " + e); + throw new ProtocolException("problem parsing stats", e); } catch (NumberFormatException e) { - throw new IllegalStateException("problem parsing stats: " + e); - } catch (IOException e) { - throw new IllegalStateException("problem parsing stats: " + e); + throw new ProtocolException("problem parsing stats", e); } finally { IoUtils.closeQuietly(reader); StrictMode.setThreadPolicy(savedPolicy); @@ -166,17 +164,33 @@ public class NetworkStatsFactory { return stats; } - public NetworkStats readNetworkStatsDetail() { + public NetworkStats readNetworkStatsDetail() throws IOException { return readNetworkStatsDetail(UID_ALL); } + public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException { + if (USE_NATIVE_PARSING) { + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); + if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) { + throw new IOException("Failed to parse network stats"); + } + if (SANITY_CHECK_NATIVE) { + final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid); + assertEquals(javaStats, stats); + } + return stats; + } else { + return javaReadNetworkStatsDetail(mStatsXtUid, limitUid); + } + } + /** - * Parse and return {@link NetworkStats} with UID-level details. Values - * monotonically increase since device boot. - * - * @throws IllegalStateException when problem parsing stats. + * Parse and return {@link NetworkStats} with UID-level details. Values are + * expected to monotonically increase since device boot. */ - public NetworkStats readNetworkStatsDetail(int limitUid) throws IllegalStateException { + @VisibleForTesting + public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid) + throws IOException { final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); @@ -188,13 +202,13 @@ public class NetworkStatsFactory { ProcFileReader reader = null; try { // open and consume header line - reader = new ProcFileReader(new FileInputStream(mStatsXtUid)); + reader = new ProcFileReader(new FileInputStream(detailPath)); reader.finishLine(); while (reader.hasMoreData()) { idx = reader.nextInt(); if (idx != lastIdx + 1) { - throw new IllegalStateException( + throw new ProtocolException( "inconsistent idx=" + idx + " after lastIdx=" + lastIdx); } lastIdx = idx; @@ -215,11 +229,9 @@ public class NetworkStatsFactory { reader.finishLine(); } } catch (NullPointerException e) { - throw new IllegalStateException("problem parsing idx " + idx, e); + throw new ProtocolException("problem parsing idx " + idx, e); } catch (NumberFormatException e) { - throw new IllegalStateException("problem parsing idx " + idx, e); - } catch (IOException e) { - throw new IllegalStateException("problem parsing idx " + idx, e); + throw new ProtocolException("problem parsing idx " + idx, e); } finally { IoUtils.closeQuietly(reader); StrictMode.setThreadPolicy(savedPolicy); @@ -227,4 +239,30 @@ public class NetworkStatsFactory { return stats; } + + public void assertEquals(NetworkStats expected, NetworkStats actual) { + if (expected.size() != actual.size()) { + throw new AssertionError( + "Expected size " + expected.size() + ", actual size " + actual.size()); + } + + NetworkStats.Entry expectedRow = null; + NetworkStats.Entry actualRow = null; + for (int i = 0; i < expected.size(); i++) { + expectedRow = expected.getValues(i, expectedRow); + actualRow = actual.getValues(i, actualRow); + if (!expectedRow.equals(actualRow)) { + throw new AssertionError( + "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow); + } + } + } + + /** + * Parse statistics from file into given {@link NetworkStats} object. Values + * are expected to monotonically increase since device boot. + */ + @VisibleForTesting + public static native int nativeReadNetworkStatsDetail( + NetworkStats stats, String path, int limitUid); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 94e7a06..04b9884 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -16,14 +16,11 @@ package com.android.internal.os; -import static android.net.NetworkStats.IFACE_ALL; -import static android.net.NetworkStats.UID_ALL; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; -import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkStats; import android.os.BatteryManager; @@ -49,7 +46,6 @@ import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; -import com.android.internal.R; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.JournaledFile; import com.google.android.collect.Sets; @@ -87,7 +83,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 62 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 64 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -356,8 +352,8 @@ public final class BatteryStatsImpl extends BatteryStats { } public static interface Unpluggable { - void unplug(long batteryUptime, long batteryRealtime); - void plug(long batteryUptime, long batteryRealtime); + void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime); + void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime); } /** @@ -392,12 +388,12 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mUnpluggedCount); } - public void unplug(long batteryUptime, long batteryRealtime) { + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { mUnpluggedCount = mPluggedCount; mCount.set(mPluggedCount); } - public void plug(long batteryUptime, long batteryRealtime) { + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { mPluggedCount = mCount.get(); } @@ -587,7 +583,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mUnpluggedTime); } - public void unplug(long batteryUptime, long batteryRealtime) { + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { if (DEBUG && mType < 0) { Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime + " old mUnpluggedTime=" + mUnpluggedTime @@ -602,7 +598,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void plug(long batteryUptime, long batteryRealtime) { + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { if (DEBUG && mType < 0) { Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime + " old mTotalTime=" + mTotalTime); @@ -731,7 +727,7 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mTrackingReportedValues; /* - * A sequnce counter, incremented once for each update of the stats. + * A sequence counter, incremented once for each update of the stats. */ int mUpdateVersion; @@ -786,8 +782,8 @@ public final class BatteryStatsImpl extends BatteryStats { mCurrentReportedTotalTime = totalTime; } - public void unplug(long batteryUptime, long batteryRealtime) { - super.unplug(batteryUptime, batteryRealtime); + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { + super.unplug(elapsedRealtime, batteryUptime, batteryRealtime); if (mTrackingReportedValues) { mUnpluggedReportedTotalTime = mCurrentReportedTotalTime; mUnpluggedReportedCount = mCurrentReportedCount; @@ -795,8 +791,8 @@ public final class BatteryStatsImpl extends BatteryStats { mInDischarge = true; } - public void plug(long batteryUptime, long batteryRealtime) { - super.plug(batteryUptime, batteryRealtime); + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { + super.plug(elapsedRealtime, batteryUptime, batteryRealtime); mInDischarge = false; } @@ -849,6 +845,141 @@ public final class BatteryStatsImpl extends BatteryStats { } /** + * A timer that increments in batches. It does not run for durations, but just jumps + * for a pre-determined amount. + */ + public static final class BatchTimer extends Timer { + final Uid mUid; + + /** + * The last time at which we updated the timer. This is in elapsed realtime microseconds. + */ + long mLastAddedTime; + + /** + * The last duration that we added to the timer. This is in microseconds. + */ + long mLastAddedDuration; + + /** + * Whether we are currently in a discharge cycle. + */ + boolean mInDischarge; + + BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables, + boolean inDischarge, Parcel in) { + super(type, unpluggables, in); + mUid = uid; + mLastAddedTime = in.readLong(); + mLastAddedDuration = in.readLong(); + mInDischarge = inDischarge; + } + + BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables, + boolean inDischarge) { + super(type, unpluggables); + mUid = uid; + mInDischarge = inDischarge; + } + + @Override + public void writeToParcel(Parcel out, long batteryRealtime) { + super.writeToParcel(out, batteryRealtime); + out.writeLong(mLastAddedTime); + out.writeLong(mLastAddedDuration); + } + + @Override + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { + recomputeLastDuration(SystemClock.elapsedRealtime() * 1000, false); + mInDischarge = false; + super.plug(elapsedRealtime, batteryUptime, batteryRealtime); + } + + @Override + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { + recomputeLastDuration(elapsedRealtime, false); + mInDischarge = true; + // If we are still within the last added duration, then re-added whatever remains. + if (mLastAddedTime == elapsedRealtime) { + mTotalTime += mLastAddedDuration; + } + super.unplug(elapsedRealtime, batteryUptime, batteryRealtime); + } + + @Override + public void logState(Printer pw, String prefix) { + super.logState(pw, prefix); + pw.println(prefix + "mLastAddedTime=" + mLastAddedTime + + " mLastAddedDuration=" + mLastAddedDuration); + } + + private long computeOverage(long curTime) { + if (mLastAddedTime > 0) { + return mLastTime + mLastAddedDuration - curTime; + } + return 0; + } + + private void recomputeLastDuration(long curTime, boolean abort) { + final long overage = computeOverage(curTime); + if (overage > 0) { + // Aborting before the duration ran out -- roll back the remaining + // duration. Only do this if currently discharging; otherwise we didn't + // actually add the time. + if (mInDischarge) { + mTotalTime -= overage; + } + if (abort) { + mLastAddedTime = 0; + } else { + mLastAddedTime = curTime; + mLastAddedDuration -= overage; + } + } + } + + public void addDuration(BatteryStatsImpl stats, long durationMillis) { + final long now = SystemClock.elapsedRealtime() * 1000; + recomputeLastDuration(now, true); + mLastAddedTime = now; + mLastAddedDuration = durationMillis * 1000; + if (mInDischarge) { + mTotalTime += mLastAddedDuration; + mCount++; + } + } + + public void abortLastDuration(BatteryStatsImpl stats) { + final long now = SystemClock.elapsedRealtime() * 1000; + recomputeLastDuration(now, true); + } + + @Override + protected int computeCurrentCountLocked() { + return mCount; + } + + @Override + protected long computeRunTimeLocked(long curBatteryRealtime) { + final long overage = computeOverage(SystemClock.elapsedRealtime() * 1000); + if (overage > 0) { + return mTotalTime = overage; + } + return mTotalTime; + } + + @Override + boolean reset(BatteryStatsImpl stats, boolean detachIfReset) { + final long now = SystemClock.elapsedRealtime() * 1000; + recomputeLastDuration(now, true); + boolean stillActive = mLastAddedTime == now; + super.reset(stats, !stillActive && detachIfReset); + return !stillActive; + } + } + + /** * State for keeping track of timing information. */ public static final class StopwatchTimer extends Timer { @@ -902,12 +1033,12 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mUpdateTime); } - public void plug(long batteryUptime, long batteryRealtime) { + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { if (mNesting > 0) { if (DEBUG && mType < 0) { Log.v(TAG, "old mUpdateTime=" + mUpdateTime); } - super.plug(batteryUptime, batteryRealtime); + super.plug(elapsedRealtime, batteryUptime, batteryRealtime); mUpdateTime = batteryRealtime; if (DEBUG && mType < 0) { Log.v(TAG, "new mUpdateTime=" + mUpdateTime); @@ -1443,7 +1574,7 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryOverflow = false; } - public void doUnplugLocked(long batteryUptime, long batteryRealtime) { + public void doUnplugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) { NetworkStats.Entry entry = null; // Track UID data usage @@ -1462,7 +1593,7 @@ public final class BatteryStatsImpl extends BatteryStats { } for (int i = mUnpluggables.size() - 1; i >= 0; i--) { - mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime); + mUnpluggables.get(i).unplug(elapsedRealtime, batteryUptime, batteryRealtime); } // Track both mobile and total overall data @@ -1483,7 +1614,7 @@ public final class BatteryStatsImpl extends BatteryStats { mBluetoothPingCount = 0; } - public void doPlugLocked(long batteryUptime, long batteryRealtime) { + public void doPlugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) { NetworkStats.Entry entry = null; for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { @@ -1498,7 +1629,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } for (int i = mUnpluggables.size() - 1; i >= 0; i--) { - mUnpluggables.get(i).plug(batteryUptime, batteryRealtime); + mUnpluggables.get(i).plug(elapsedRealtime, batteryUptime, batteryRealtime); } // Track both mobile and total overall data @@ -2109,6 +2240,14 @@ public final class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteVideoTurnedOffLocked(); } + public void noteVibratorOnLocked(int uid, long durationMillis) { + getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis); + } + + public void noteVibratorOffLocked(int uid) { + getUidStatsLocked(uid).noteVibratorOffLocked(); + } + public void noteWifiRunningLocked(WorkSource ws) { if (!mGlobalWifiRunning) { mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG; @@ -2402,6 +2541,8 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mVideoTurnedOn; StopwatchTimer mVideoTurnedOnTimer; + BatchTimer mVibratorOnTimer; + Counter[] mUserActivityCounters; /** @@ -2439,10 +2580,6 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiScanTimers, mUnpluggables); mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimers, mUnpluggables); - mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON, - null, mUnpluggables); - mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON, - null, mUnpluggables); } @Override @@ -2587,15 +2724,19 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public StopwatchTimer createAudioTurnedOnTimerLocked() { + if (mAudioTurnedOnTimer == null) { + mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON, + null, mUnpluggables); + } + return mAudioTurnedOnTimer; + } + @Override public void noteAudioTurnedOnLocked() { if (!mAudioTurnedOn) { mAudioTurnedOn = true; - if (mAudioTurnedOnTimer == null) { - mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON, - null, mUnpluggables); - } - mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + createAudioTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this); } } @@ -2603,19 +2744,25 @@ public final class BatteryStatsImpl extends BatteryStats { public void noteAudioTurnedOffLocked() { if (mAudioTurnedOn) { mAudioTurnedOn = false; - mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + if (mAudioTurnedOnTimer != null) { + mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + } + + public StopwatchTimer createVideoTurnedOnTimerLocked() { + if (mVideoTurnedOnTimer == null) { + mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON, + null, mUnpluggables); } + return mVideoTurnedOnTimer; } @Override public void noteVideoTurnedOnLocked() { if (!mVideoTurnedOn) { mVideoTurnedOn = true; - if (mVideoTurnedOnTimer == null) { - mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON, - null, mUnpluggables); - } - mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + createVideoTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this); } } @@ -2623,7 +2770,27 @@ public final class BatteryStatsImpl extends BatteryStats { public void noteVideoTurnedOffLocked() { if (mVideoTurnedOn) { mVideoTurnedOn = false; - mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + if (mVideoTurnedOnTimer != null) { + mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + } + + public BatchTimer createVibratorOnTimerLocked() { + if (mVibratorOnTimer == null) { + mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, + mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal); + } + return mVibratorOnTimer; + } + + public void noteVibratorOnLocked(long durationMillis) { + createVibratorOnTimerLocked().addDuration(BatteryStatsImpl.this, durationMillis); + } + + public void noteVibratorOffLocked() { + if (mVibratorOnTimer != null) { + mVibratorOnTimer.abortLastDuration(BatteryStatsImpl.this); } } @@ -2677,6 +2844,11 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public Timer getVibratorOnTimer() { + return mVibratorOnTimer; + } + + @Override public void noteUserActivityLocked(int type) { if (mUserActivityCounters == null) { initUserActivityLocked(); @@ -2747,6 +2919,14 @@ public final class BatteryStatsImpl extends BatteryStats { active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false); active |= mVideoTurnedOn; } + if (mVibratorOnTimer != null) { + if (mVibratorOnTimer.reset(BatteryStatsImpl.this, false)) { + mVibratorOnTimer.detach(); + mVibratorOnTimer = null; + } else { + active = true; + } + } mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0; mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0; @@ -2832,9 +3012,11 @@ public final class BatteryStatsImpl extends BatteryStats { } if (mAudioTurnedOnTimer != null) { mAudioTurnedOnTimer.detach(); + mAudioTurnedOnTimer = null; } if (mVideoTurnedOnTimer != null) { mVideoTurnedOnTimer.detach(); + mVideoTurnedOnTimer = null; } if (mUserActivityCounters != null) { for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) { @@ -2917,6 +3099,12 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + if (mVibratorOnTimer != null) { + out.writeInt(1); + mVibratorOnTimer.writeToParcel(out, batteryRealtime); + } else { + out.writeInt(0); + } if (mUserActivityCounters != null) { out.writeInt(1); for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) { @@ -3016,6 +3204,12 @@ public final class BatteryStatsImpl extends BatteryStats { mVideoTurnedOnTimer = null; } if (in.readInt() != 0) { + mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, + mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal, in); + } else { + mVibratorOnTimer = null; + } + if (in.readInt() != 0) { mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES]; for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) { mUserActivityCounters[i] = new Counter(mUnpluggables, in); @@ -3256,14 +3450,14 @@ public final class BatteryStatsImpl extends BatteryStats { mSpeedBins = new SamplingCounter[getCpuSpeedSteps()]; } - public void unplug(long batteryUptime, long batteryRealtime) { + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { mUnpluggedUserTime = mUserTime; mUnpluggedSystemTime = mSystemTime; mUnpluggedStarts = mStarts; mUnpluggedForegroundTime = mForegroundTime; } - public void plug(long batteryUptime, long batteryRealtime) { + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { } void detach() { @@ -3550,11 +3744,11 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggables.add(this); } - public void unplug(long batteryUptime, long batteryRealtime) { + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { mUnpluggedWakeups = mWakeups; } - public void plug(long batteryUptime, long batteryRealtime) { + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { } void detach() { @@ -3712,13 +3906,13 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggables.add(this); } - public void unplug(long batteryUptime, long batteryRealtime) { + public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { mUnpluggedStartTime = getStartTimeToNowLocked(batteryUptime); mUnpluggedStarts = mStarts; mUnpluggedLaunches = mLaunches; } - public void plug(long batteryUptime, long batteryRealtime) { + public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) { } void detach() { @@ -4367,7 +4561,7 @@ public final class BatteryStatsImpl extends BatteryStats { } mDischargeAmountScreenOn = 0; mDischargeAmountScreenOff = 0; - doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); + doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); } else { updateKernelWakelocksLocked(); mHistoryCur.batteryLevel = (byte)level; @@ -4383,7 +4577,7 @@ public final class BatteryStatsImpl extends BatteryStats { mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level; } updateDischargeScreenLevelsLocked(mScreenOn, mScreenOn); - doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime)); + doPlugLocked(realtime, getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime)); } if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) { if (mFile != null) { @@ -5161,11 +5355,14 @@ public final class BatteryStatsImpl extends BatteryStats { } u.mAudioTurnedOn = false; if (in.readInt() != 0) { - u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in); + u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in); } u.mVideoTurnedOn = false; if (in.readInt() != 0) { - u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in); + u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in); + } + if (in.readInt() != 0) { + u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in); } if (in.readInt() != 0) { @@ -5367,6 +5564,12 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + if (u.mVibratorOnTimer != null) { + out.writeInt(1); + u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); + } else { + out.writeInt(0); + } if (u.mUserActivityCounters == null) { out.writeInt(0); @@ -5753,7 +5956,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { try { mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev(); - } catch (IllegalStateException e) { + } catch (IOException e) { Log.wtf(TAG, "problem reading network stats", e); } } @@ -5777,7 +5980,7 @@ public final class BatteryStatsImpl extends BatteryStats { try { mNetworkDetailCache = mNetworkStatsFactory .readNetworkStatsDetail().groupedByUid(); - } catch (IllegalStateException e) { + } catch (IOException e) { Log.wtf(TAG, "problem reading network stats", e); } } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index d24513a..fd7e3b0 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -76,18 +76,6 @@ class ZygoteConnection { private final String peerSecurityContext; /** - * A long-lived reference to the original command socket used to launch - * this peer. If "peer wait" mode is specified, the process that requested - * the new VM instance intends to track the lifetime of the spawned instance - * via the command socket. In this case, the command socket is closed - * in the Zygote and placed here in the spawned instance so that it will - * not be collected and finalized. This field remains null at all times - * in the original Zygote process, and in all spawned processes where - * "peer-wait" mode was not requested. - */ - private static LocalSocket sPeerWaitSocket = null; - - /** * Constructs instance from connected socket. * * @param socket non-null; connected socket @@ -298,11 +286,6 @@ class ZygoteConnection { * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call. * <code>r</code> is the resource, <code>c</code> and <code>m</code> * are the settings for current and max value.</i> - * <li> --peer-wait indicates that the command socket should - * be inherited by (and set to close-on-exec in) the spawned process - * and used to track the lifetime of that process. The spawning process - * then exits. Without this flag, it is retained by the spawning process - * (and closed in the child) in expectation of a new spawn request. * <li> --classpath=<i>colon-separated classpath</i> indicates * that the specified class (which must b first non-flag argument) should * be loaded from jar files in the specified classpath. Incompatible with @@ -330,9 +313,6 @@ class ZygoteConnection { /** from --setgroups */ int[] gids; - /** from --peer-wait */ - boolean peerWait; - /** * From --enable-debugger, --enable-checkjni, --enable-assert, * --enable-safemode, and --enable-jni-logging. @@ -437,8 +417,6 @@ class ZygoteConnection { debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; } else if (arg.equals("--enable-assert")) { debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; - } else if (arg.equals("--peer-wait")) { - peerWait = true; } else if (arg.equals("--runtime-init")) { runtimeInit = true; } else if (arg.startsWith("--seinfo=")) { @@ -897,23 +875,8 @@ class ZygoteConnection { FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller { - /* - * Close the socket, unless we're in "peer wait" mode, in which - * case it's used to track the liveness of this process. - */ - - if (parsedArgs.peerWait) { - try { - ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true); - sPeerWaitSocket = mSocket; - } catch (IOException ex) { - Log.e(TAG, "Zygote Child: error setting peer wait " - + "socket to be close-on-exec", ex); - } - } else { - closeSocket(); - ZygoteInit.closeServerSocket(); - } + closeSocket(); + ZygoteInit.closeServerSocket(); if (descriptors != null) { try { @@ -1044,18 +1007,6 @@ class ZygoteConnection { return true; } - /* - * If the peer wants to use the socket to wait on the - * newly spawned process, then we're all done. - */ - if (parsedArgs.peerWait) { - try { - mSocket.close(); - } catch (IOException ex) { - Log.e(TAG, "Zygote: error closing sockets", ex); - } - return true; - } return false; } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9e43749..7eddc9c 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -19,10 +19,8 @@ package com.android.internal.os; import static libcore.io.OsConstants.S_IRWXG; import static libcore.io.OsConstants.S_IRWXO; -import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; import android.net.LocalServerSocket; import android.os.Debug; import android.os.Process; @@ -88,12 +86,6 @@ public class ZygoteInit { static final int GC_LOOP_COUNT = 10; /** - * If true, zygote forks for each peer. If false, a select loop is used - * inside a single process. The latter is preferred. - */ - private static final boolean ZYGOTE_FORK_MODE = false; - - /** * The name of a resource file that contains classes to preload. */ private static final String PRELOADED_CLASSES = "preloaded-classes"; @@ -549,11 +541,7 @@ public class ZygoteInit { Log.i(TAG, "Accepting command socket connections"); - if (ZYGOTE_FORK_MODE) { - runForkMode(); - } else { - runSelectLoopMode(); - } + runSelectLoop(); closeServerSocket(); } catch (MethodAndArgsCaller caller) { @@ -566,44 +554,6 @@ public class ZygoteInit { } /** - * Runs the zygote in accept-and-fork mode. In this mode, each peer - * gets its own zygote spawner process. This code is retained for - * reference only. - * - * @throws MethodAndArgsCaller in a child process when a main() should - * be executed. - */ - private static void runForkMode() throws MethodAndArgsCaller { - while (true) { - ZygoteConnection peer = acceptCommandPeer(); - - int pid; - - pid = Zygote.fork(); - - if (pid == 0) { - // The child process should handle the peer requests - - // The child does not accept any more connections - try { - sServerSocket.close(); - } catch (IOException ex) { - Log.e(TAG, "Zygote Child: error closing sockets", ex); - } finally { - sServerSocket = null; - } - - peer.run(); - break; - } else if (pid > 0) { - peer.closeSocket(); - } else { - throw new RuntimeException("Error invoking fork()"); - } - } - } - - /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. @@ -611,9 +561,9 @@ public class ZygoteInit { * @throws MethodAndArgsCaller in a child process when a main() should * be executed. */ - private static void runSelectLoopMode() throws MethodAndArgsCaller { - ArrayList<FileDescriptor> fds = new ArrayList(); - ArrayList<ZygoteConnection> peers = new ArrayList(); + private static void runSelectLoop() throws MethodAndArgsCaller { + ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); + ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); FileDescriptor[] fdArray = new FileDescriptor[4]; fds.add(sServerSocket.getFileDescriptor()); @@ -734,17 +684,6 @@ public class ZygoteInit { throws IOException; /** - * Sets the permitted and effective capability sets of this process. - * - * @param permittedCapabilities permitted set - * @param effectiveCapabilities effective set - * @throws IOException on error - */ - static native void setCapabilities( - long permittedCapabilities, - long effectiveCapabilities) throws IOException; - - /** * Invokes select() on the provider array of file descriptors (selecting * for readability only). Array elements of null are ignored. * diff --git a/core/java/com/android/internal/policy/PolicyManager.java b/core/java/com/android/internal/policy/PolicyManager.java index 5274e54..462b3a9 100644 --- a/core/java/com/android/internal/policy/PolicyManager.java +++ b/core/java/com/android/internal/policy/PolicyManager.java @@ -22,8 +22,6 @@ import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManagerPolicy; -import com.android.internal.policy.IPolicy; - /** * {@hide} */ diff --git a/core/java/com/android/internal/statusbar/INotificationListener.java b/core/java/com/android/internal/statusbar/INotificationListener.java new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/core/java/com/android/internal/statusbar/INotificationListener.java diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java index a91aa3c..23e87fc 100644 --- a/core/java/com/android/internal/statusbar/StatusBarNotification.java +++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java @@ -21,23 +21,13 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; -/* -boolean clearable = !n.ongoingEvent && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0); - - -// TODO: make this restriction do something smarter like never fill -// more than two screens. "Why would anyone need more than 80 characters." :-/ -final int maxTickerLen = 80; -if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) { - truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen); -} -*/ - /** - * Class encapsulating a Notification. Sent by the NotificationManagerService to the IStatusBar (in System UI). + * Class encapsulating a Notification. Sent by the NotificationManagerService to clients including + * the IStatusBar (in System UI). */ public class StatusBarNotification implements Parcelable { public final String pkg; + public final String basePkg; public final int id; public final String tag; public final int uid; @@ -47,6 +37,7 @@ public class StatusBarNotification implements Parcelable { public final Notification notification; public final int score; public final UserHandle user; + public final long postTime; /** This is temporarily needed for the JB MR1 PDK. */ @Deprecated @@ -57,10 +48,23 @@ public class StatusBarNotification implements Parcelable { public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user) { + this(pkg, null, id, tag, uid, initialPid, score, notification, user); + } + + public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid, + int initialPid, int score, Notification notification, UserHandle user) { + this(pkg, basePkg, id, tag, uid, initialPid, score, notification, user, + System.currentTimeMillis()); + } + + public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid, + int initialPid, int score, Notification notification, UserHandle user, + long postTime) { if (pkg == null) throw new NullPointerException(); if (notification == null) throw new NullPointerException(); this.pkg = pkg; + this.basePkg = pkg; this.id = id; this.tag = tag; this.uid = uid; @@ -69,10 +73,13 @@ public class StatusBarNotification implements Parcelable { this.notification = notification; this.user = user; this.notification.setUser(user); + + this.postTime = postTime; } public StatusBarNotification(Parcel in) { this.pkg = in.readString(); + this.basePkg = in.readString(); this.id = in.readInt(); if (in.readInt() != 0) { this.tag = in.readString(); @@ -84,11 +91,13 @@ public class StatusBarNotification implements Parcelable { this.score = in.readInt(); this.notification = new Notification(in); this.user = UserHandle.readFromParcel(in); - this.notification.setUser(user); + this.notification.setUser(this.user); + this.postTime = in.readLong(); } public void writeToParcel(Parcel out, int flags) { out.writeString(this.pkg); + out.writeString(this.basePkg); out.writeInt(this.id); if (this.tag != null) { out.writeInt(1); @@ -101,6 +110,8 @@ public class StatusBarNotification implements Parcelable { out.writeInt(this.score); this.notification.writeToParcel(out, flags); user.writeToParcel(out, flags); + + out.writeLong(this.postTime); } public int describeContents() { @@ -123,14 +134,17 @@ public class StatusBarNotification implements Parcelable { @Override public StatusBarNotification clone() { - return new StatusBarNotification(this.pkg, this.id, this.tag, this.uid, this.initialPid, - this.score, this.notification.clone(), this.user); + return new StatusBarNotification(this.pkg, this.basePkg, + this.id, this.tag, this.uid, this.initialPid, + this.score, this.notification.clone(), this.user, this.postTime); } @Override public String toString() { - return "StatusBarNotification(pkg=" + pkg + " id=" + id + " tag=" + tag + " score=" + score - + " notn=" + notification + " user=" + user + ")"; + return String.format( + "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d: %s)", + this.pkg, this.user, this.id, this.tag, + this.score, this.notification); } public boolean isOngoing() { diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java index 592a8fa..7a04080 100644 --- a/core/java/com/android/internal/util/FastXmlSerializer.java +++ b/core/java/com/android/internal/util/FastXmlSerializer.java @@ -50,6 +50,8 @@ public class FastXmlSerializer implements XmlSerializer { private static final int BUFFER_LEN = 8192; + private static String sSpace = " "; + private final char[] mText = new char[BUFFER_LEN]; private int mPos; @@ -59,8 +61,12 @@ public class FastXmlSerializer implements XmlSerializer { private CharsetEncoder mCharset; private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); + private boolean mIndent = false; private boolean mInTag; + private int mNesting = 0; + private boolean mLineStart = true; + private void append(char c) throws IOException { int pos = mPos; if (pos >= (BUFFER_LEN-1)) { @@ -113,6 +119,14 @@ public class FastXmlSerializer implements XmlSerializer { append(str, 0, str.length()); } + private void appendIndent(int indent) throws IOException { + indent *= 4; + if (indent > sSpace.length()) { + indent = sSpace.length(); + } + append(sSpace, 0, indent); + } + private void escapeAndAppendString(final String string) throws IOException { final int N = string.length(); final char NE = (char)ESCAPE_TABLE.length; @@ -161,6 +175,7 @@ public class FastXmlSerializer implements XmlSerializer { escapeAndAppendString(value); append('"'); + mLineStart = false; return this; } @@ -185,9 +200,13 @@ public class FastXmlSerializer implements XmlSerializer { public XmlSerializer endTag(String namespace, String name) throws IOException, IllegalArgumentException, IllegalStateException { + mNesting--; if (mInTag) { append(" />\n"); } else { + if (mIndent && mLineStart) { + appendIndent(mNesting); + } append("</"); if (namespace != null) { append(namespace); @@ -196,6 +215,7 @@ public class FastXmlSerializer implements XmlSerializer { append(name); append(">\n"); } + mLineStart = true; mInTag = false; return this; } @@ -278,6 +298,7 @@ public class FastXmlSerializer implements XmlSerializer { public void setFeature(String name, boolean state) throws IllegalArgumentException, IllegalStateException { if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) { + mIndent = true; return; } throw new UnsupportedOperationException(); @@ -325,6 +346,7 @@ public class FastXmlSerializer implements XmlSerializer { IllegalArgumentException, IllegalStateException { append("<?xml version='1.0' encoding='utf-8' standalone='" + (standalone ? "yes" : "no") + "' ?>\n"); + mLineStart = true; } public XmlSerializer startTag(String namespace, String name) throws IOException, @@ -332,6 +354,10 @@ public class FastXmlSerializer implements XmlSerializer { if (mInTag) { append(">\n"); } + if (mIndent) { + appendIndent(mNesting); + } + mNesting++; append('<'); if (namespace != null) { append(namespace); @@ -339,6 +365,7 @@ public class FastXmlSerializer implements XmlSerializer { } append(name); mInTag = true; + mLineStart = false; return this; } @@ -349,6 +376,9 @@ public class FastXmlSerializer implements XmlSerializer { mInTag = false; } escapeAndAppendString(buf, start, len); + if (mIndent) { + mLineStart = buf[start+len-1] == '\n'; + } return this; } @@ -359,6 +389,9 @@ public class FastXmlSerializer implements XmlSerializer { mInTag = false; } escapeAndAppendString(text); + if (mIndent) { + mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n'); + } return this; } diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java index dd5918b..d01a817 100644 --- a/core/java/com/android/internal/util/IndentingPrintWriter.java +++ b/core/java/com/android/internal/util/IndentingPrintWriter.java @@ -21,29 +21,47 @@ import java.io.Writer; /** * Lightweight wrapper around {@link PrintWriter} that automatically indents - * newlines based on internal state. Delays writing indent until first actual - * write on a newline, enabling indent modification after newline. + * newlines based on internal state. It also automatically wraps long lines + * based on given line length. + * <p> + * Delays writing indent until first actual write on a newline, enabling indent + * modification after newline. */ public class IndentingPrintWriter extends PrintWriter { - private final String mIndent; + private final String mSingleIndent; + private final int mWrapLength; - private StringBuilder mBuilder = new StringBuilder(); - private char[] mCurrent; + /** Mutable version of current indent */ + private StringBuilder mIndentBuilder = new StringBuilder(); + /** Cache of current {@link #mIndentBuilder} value */ + private char[] mCurrentIndent; + /** Length of current line being built, excluding any indent */ + private int mCurrentLength; + + /** + * Flag indicating if we're currently sitting on an empty line, and that + * next write should be prefixed with the current indent. + */ private boolean mEmptyLine = true; - public IndentingPrintWriter(Writer writer, String indent) { + public IndentingPrintWriter(Writer writer, String singleIndent) { + this(writer, singleIndent, -1); + } + + public IndentingPrintWriter(Writer writer, String singleIndent, int wrapLength) { super(writer); - mIndent = indent; + mSingleIndent = singleIndent; + mWrapLength = wrapLength; } public void increaseIndent() { - mBuilder.append(mIndent); - mCurrent = null; + mIndentBuilder.append(mSingleIndent); + mCurrentIndent = null; } public void decreaseIndent() { - mBuilder.delete(0, mIndent.length()); - mCurrent = null; + mIndentBuilder.delete(0, mSingleIndent.length()); + mCurrentIndent = null; } public void printPair(String key, Object value) { @@ -52,33 +70,56 @@ public class IndentingPrintWriter extends PrintWriter { @Override public void write(char[] buf, int offset, int count) { + final int indentLength = mIndentBuilder.length(); final int bufferEnd = offset + count; int lineStart = offset; int lineEnd = offset; + + // March through incoming buffer looking for newlines while (lineEnd < bufferEnd) { char ch = buf[lineEnd++]; + mCurrentLength++; if (ch == '\n') { - writeIndent(); + maybeWriteIndent(); super.write(buf, lineStart, lineEnd - lineStart); lineStart = lineEnd; mEmptyLine = true; + mCurrentLength = 0; + } + + // Wrap if we've pushed beyond line length + if (mWrapLength > 0 && mCurrentLength >= mWrapLength - indentLength) { + if (!mEmptyLine) { + // Give ourselves a fresh line to work with + super.write('\n'); + mEmptyLine = true; + mCurrentLength = lineEnd - lineStart; + } else { + // We need more than a dedicated line, slice it hard + maybeWriteIndent(); + super.write(buf, lineStart, lineEnd - lineStart); + super.write('\n'); + mEmptyLine = true; + lineStart = lineEnd; + mCurrentLength = 0; + } } } if (lineStart != lineEnd) { - writeIndent(); + maybeWriteIndent(); super.write(buf, lineStart, lineEnd - lineStart); } } - private void writeIndent() { + private void maybeWriteIndent() { if (mEmptyLine) { mEmptyLine = false; - if (mBuilder.length() != 0) { - if (mCurrent == null) { - mCurrent = mBuilder.toString().toCharArray(); + if (mIndentBuilder.length() != 0) { + if (mCurrentIndent == null) { + mCurrentIndent = mIndentBuilder.toString().toCharArray(); } - super.write(mCurrent, 0, mCurrent.length); + super.write(mCurrentIndent, 0, mCurrentIndent.length); } } } diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java index 72e1f0f..81571fe 100644 --- a/core/java/com/android/internal/util/ProcFileReader.java +++ b/core/java/com/android/internal/util/ProcFileReader.java @@ -19,6 +19,7 @@ package com.android.internal.util; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.net.ProtocolException; import java.nio.charset.Charsets; /** @@ -82,12 +83,15 @@ public class ProcFileReader implements Closeable { } /** - * Find buffer index of next token delimiter, usually space or newline. Will - * fill buffer as needed. + * Find buffer index of next token delimiter, usually space or newline. + * Fills buffer as needed. + * + * @return Index of next delimeter, otherwise -1 if no tokens remain on + * current line. */ private int nextTokenIndex() throws IOException { if (mLineFinished) { - throw new IOException("no tokens remaining on current line"); + return -1; } int i = 0; @@ -105,7 +109,7 @@ public class ProcFileReader implements Closeable { } } while (fillBuf() > 0); - throw new IOException("end of stream while looking for token boundary"); + throw new ProtocolException("End of stream while looking for token boundary"); } /** @@ -136,7 +140,7 @@ public class ProcFileReader implements Closeable { } } while (fillBuf() > 0); - throw new IOException("end of stream while looking for line boundary"); + throw new ProtocolException("End of stream while looking for line boundary"); } /** @@ -144,9 +148,11 @@ public class ProcFileReader implements Closeable { */ public String nextString() throws IOException { final int tokenIndex = nextTokenIndex(); - final String s = new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII); - consumeBuf(tokenIndex + 1); - return s; + if (tokenIndex == -1) { + throw new ProtocolException("Missing required string"); + } else { + return parseAndConsumeString(tokenIndex); + } } /** @@ -154,6 +160,33 @@ public class ProcFileReader implements Closeable { */ public long nextLong() throws IOException { final int tokenIndex = nextTokenIndex(); + if (tokenIndex == -1) { + throw new ProtocolException("Missing required long"); + } else { + return parseAndConsumeLong(tokenIndex); + } + } + + /** + * Parse and return next token as base-10 encoded {@code long}, or return + * the given default value if no remaining tokens on current line. + */ + public long nextOptionalLong(long def) throws IOException { + final int tokenIndex = nextTokenIndex(); + if (tokenIndex == -1) { + return def; + } else { + return parseAndConsumeLong(tokenIndex); + } + } + + private String parseAndConsumeString(int tokenIndex) throws IOException { + final String s = new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII); + consumeBuf(tokenIndex + 1); + return s; + } + + private long parseAndConsumeLong(int tokenIndex) throws IOException { final boolean negative = mBuffer[0] == '-'; // TODO: refactor into something like IntegralToString @@ -193,6 +226,7 @@ public class ProcFileReader implements Closeable { return (int) value; } + @Override public void close() throws IOException { mStream.close(); } diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index 0ea7b83..28fd05f 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -27,6 +27,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; import java.util.HashMap; import java.util.Vector; @@ -35,7 +36,7 @@ import java.util.Vector; * * <p>The state machine defined here is a hierarchical state machine which processes messages * and can have states arranged hierarchically.</p> - * + * * <p>A state is a <code>State</code> object and must implement * <code>processMessage</code> and optionally <code>enter/exit/getName</code>. * The enter/exit methods are equivalent to the construction and destruction @@ -81,8 +82,8 @@ import java.util.Vector; * machine will cause <code>haltedProcessMessage</code> to be invoked.</p> * * <p>If it is desirable to completely stop the state machine call <code>quit</code> or - * <code>abort</code>. These will call <code>exit</code> of the current state and its parents, call - * <code>onQuiting</code> and then exit Thread/Loopers.</p> + * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents, + * call <code>onQuiting</code> and then exit Thread/Loopers.</p> * * <p>In addition to <code>processMessage</code> each <code>State</code> has * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p> @@ -148,7 +149,7 @@ class HelloWorld extends StateMachine { class State1 extends State { @Override public boolean processMessage(Message message) { - Log.d(TAG, "Hello World"); + log("Hello World"); return HANDLED; } } @@ -232,8 +233,6 @@ state mP2 { * <p>The implementation is below and also in StateMachineTest:</p> <code> class Hsm1 extends StateMachine { - private static final String TAG = "hsm1"; - public static final int CMD_1 = 1; public static final int CMD_2 = 2; public static final int CMD_3 = 3; @@ -241,16 +240,16 @@ class Hsm1 extends StateMachine { public static final int CMD_5 = 5; public static Hsm1 makeHsm1() { - Log.d(TAG, "makeHsm1 E"); + log("makeHsm1 E"); Hsm1 sm = new Hsm1("hsm1"); sm.start(); - Log.d(TAG, "makeHsm1 X"); + log("makeHsm1 X"); return sm; } Hsm1(String name) { super(name); - Log.d(TAG, "ctor E"); + log("ctor E"); // Add states, use indentation to show hierarchy addState(mP1); @@ -260,16 +259,16 @@ class Hsm1 extends StateMachine { // Set the initial state setInitialState(mS1); - Log.d(TAG, "ctor X"); + log("ctor X"); } class P1 extends State { @Override public void enter() { - Log.d(TAG, "mP1.enter"); + log("mP1.enter"); } @Override public boolean processMessage(Message message) { boolean retVal; - Log.d(TAG, "mP1.processMessage what=" + message.what); + log("mP1.processMessage what=" + message.what); switch(message.what) { case CMD_2: // CMD_2 will arrive in mS2 before CMD_3 @@ -286,16 +285,16 @@ class Hsm1 extends StateMachine { return retVal; } @Override public void exit() { - Log.d(TAG, "mP1.exit"); + log("mP1.exit"); } } class S1 extends State { @Override public void enter() { - Log.d(TAG, "mS1.enter"); + log("mS1.enter"); } @Override public boolean processMessage(Message message) { - Log.d(TAG, "S1.processMessage what=" + message.what); + log("S1.processMessage what=" + message.what); if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); @@ -306,17 +305,17 @@ class Hsm1 extends StateMachine { } } @Override public void exit() { - Log.d(TAG, "mS1.exit"); + log("mS1.exit"); } } class S2 extends State { @Override public void enter() { - Log.d(TAG, "mS2.enter"); + log("mS2.enter"); } @Override public boolean processMessage(Message message) { boolean retVal; - Log.d(TAG, "mS2.processMessage what=" + message.what); + log("mS2.processMessage what=" + message.what); switch(message.what) { case(CMD_2): sendMessage(obtainMessage(CMD_4)); @@ -334,17 +333,17 @@ class Hsm1 extends StateMachine { return retVal; } @Override public void exit() { - Log.d(TAG, "mS2.exit"); + log("mS2.exit"); } } class P2 extends State { @Override public void enter() { - Log.d(TAG, "mP2.enter"); + log("mP2.enter"); sendMessage(obtainMessage(CMD_5)); } @Override public boolean processMessage(Message message) { - Log.d(TAG, "P2.processMessage what=" + message.what); + log("P2.processMessage what=" + message.what); switch(message.what) { case(CMD_3): break; @@ -357,13 +356,13 @@ class Hsm1 extends StateMachine { return HANDLED; } @Override public void exit() { - Log.d(TAG, "mP2.exit"); + log("mP2.exit"); } } @Override void onHalting() { - Log.d(TAG, "halting"); + log("halting"); synchronized (this) { this.notifyAll(); } @@ -386,7 +385,7 @@ synchronize(hsm) { // wait for the messages to be handled hsm.wait(); } catch (InterruptedException e) { - Log.e(TAG, "exception while waiting " + e.getMessage()); + loge("exception while waiting " + e.getMessage()); } } </code> @@ -418,8 +417,7 @@ D/hsm1 ( 1999): halting </code> */ public class StateMachine { - - private static final String TAG = "StateMachine"; + // Name of the state machine and used as logging tag private String mName; /** Message.what value when quitting */ @@ -447,36 +445,44 @@ public class StateMachine { * {@hide} */ public static class LogRec { + private StateMachine mSm; private long mTime; private int mWhat; private String mInfo; - private State mState; - private State mOrgState; + private IState mState; + private IState mOrgState; + private IState mDstState; /** * Constructor * * @param msg - * @param state that handled the message + * @param state the state which handled the message * @param orgState is the first state the received the message but * did not processes the message. + * @param transToState is the state that was transitioned to after the message was + * processed. */ - LogRec(Message msg, String info, State state, State orgState) { - update(msg, info, state, orgState); + LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState, + IState transToState) { + update(sm, msg, info, state, orgState, transToState); } /** * Update the information in the record. * @param state that handled the message - * @param orgState is the first state the received the message but - * did not processes the message. + * @param orgState is the first state the received the message + * @param dstState is the state that was the transition target when logging */ - public void update(Message msg, String info, State state, State orgState) { + public void update(StateMachine sm, Message msg, String info, IState state, IState orgState, + IState dstState) { + mSm = sm; mTime = System.currentTimeMillis(); mWhat = (msg != null) ? msg.what : 0; mInfo = info; mState = state; mOrgState = orgState; + mDstState = dstState; } /** @@ -503,32 +509,40 @@ public class StateMachine { /** * @return the state that handled this message */ - public State getState() { + public IState getState() { return mState; } /** - * @return the original state that received the message. + * @return the state destination state if a transition is occurring or null if none. */ - public State getOriginalState() { - return mOrgState; + public IState getDestState() { + return mDstState; } + /** - * @return as string + * @return the original state that received the message. */ - public String toString(StateMachine sm) { + public IState getOriginalState() { + return mOrgState; + } + + @Override + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("time="); Calendar c = Calendar.getInstance(); c.setTimeInMillis(mTime); sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); - sb.append(" state="); + sb.append(" processed="); sb.append(mState == null ? "<null>" : mState.getName()); - sb.append(" orgState="); + sb.append(" org="); sb.append(mOrgState == null ? "<null>" : mOrgState.getName()); + sb.append(" dest="); + sb.append(mDstState == null ? "<null>" : mDstState.getName()); sb.append(" what="); - String what = sm.getWhatToString(mWhat); + String what = mSm != null ? mSm.getWhatToString(mWhat) : ""; if (TextUtils.isEmpty(what)) { sb.append(mWhat); sb.append("(0x"); @@ -560,10 +574,11 @@ public class StateMachine { private static final int DEFAULT_SIZE = 20; - private Vector<LogRec> mLogRecords = new Vector<LogRec>(); + private Vector<LogRec> mLogRecVector = new Vector<LogRec>(); private int mMaxSize = DEFAULT_SIZE; private int mOldestIndex = 0; private int mCount = 0; + private boolean mLogOnlyTransitions = false; /** * private constructor use add @@ -579,14 +594,22 @@ public class StateMachine { synchronized void setSize(int maxSize) { mMaxSize = maxSize; mCount = 0; - mLogRecords.clear(); + mLogRecVector.clear(); + } + + synchronized void setLogOnlyTransitions(boolean enable) { + mLogOnlyTransitions = enable; + } + + synchronized boolean logOnlyTransitions() { + return mLogOnlyTransitions; } /** * @return the number of recent records. */ synchronized int size() { - return mLogRecords.size(); + return mLogRecVector.size(); } /** @@ -600,7 +623,7 @@ public class StateMachine { * Clear the list of records. */ synchronized void cleanup() { - mLogRecords.clear(); + mLogRecVector.clear(); } /** @@ -616,7 +639,7 @@ public class StateMachine { if (nextIndex >= size()) { return null; } else { - return mLogRecords.get(nextIndex); + return mLogRecVector.get(nextIndex); } } @@ -628,18 +651,23 @@ public class StateMachine { * @param state that handled the message * @param orgState is the first state the received the message but * did not processes the message. + * @param transToState is the state that was transitioned to after the message was + * processed. + * */ - synchronized void add(Message msg, String messageInfo, State state, State orgState) { + synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state, + IState orgState, + IState transToState) { mCount += 1; - if (mLogRecords.size() < mMaxSize) { - mLogRecords.add(new LogRec(msg, messageInfo, state, orgState)); + if (mLogRecVector.size() < mMaxSize) { + mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState)); } else { - LogRec pmi = mLogRecords.get(mOldestIndex); + LogRec pmi = mLogRecVector.get(mOldestIndex); mOldestIndex += 1; if (mOldestIndex >= mMaxSize) { mOldestIndex = 0; } - pmi.update(msg, messageInfo, state, orgState); + pmi.update(sm, msg, messageInfo, state, orgState, transToState); } } } @@ -750,14 +778,16 @@ public class StateMachine { */ @Override public final void handleMessage(Message msg) { - if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what); + if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what); /** Save the current message */ mMsg = msg; + /** State that processed the message */ + State msgProcessedState = null; if (mIsConstructionCompleted) { /** Normal path */ - processMsg(msg); + msgProcessedState = processMsg(msg); } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) { /** Initial one time path. */ @@ -767,49 +797,79 @@ public class StateMachine { throw new RuntimeException("StateMachine.handleMessage: " + "The start method not called, received msg: " + msg); } - performTransitions(); + performTransitions(msgProcessedState, msg); - if (mDbg) Log.d(TAG, "handleMessage: X"); + // We need to check if mSm == null here as we could be quitting. + if (mDbg && mSm != null) mSm.log("handleMessage: X"); } /** * Do any transitions + * @param msgProcessedState is the state that processed the message */ - private void performTransitions() { + private void performTransitions(State msgProcessedState, Message msg) { /** * If transitionTo has been called, exit and then enter * the appropriate states. We loop on this to allow * enter and exit methods to use transitionTo. */ - State destState = null; - while (mDestState != null) { - if (mDbg) Log.d(TAG, "handleMessage: new destination call exit"); + State orgState = mStateStack[mStateStackTopIndex].state; - /** - * Save mDestState locally and set to null - * to know if enter/exit use transitionTo. - */ - destState = mDestState; - mDestState = null; + /** + * Record whether message needs to be logged before we transition and + * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which + * always set msg.obj to the handler. + */ + boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj); + if (mLogRecords.logOnlyTransitions()) { + /** Record only if there is a transition */ + if (mDestState != null) { + mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, + orgState, mDestState); + } + } else if (recordLogMsg) { + /** Record message */ + mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, + orgState, mDestState); + } + + State destState = mDestState; + if (destState != null) { /** - * Determine the states to exit and enter and return the - * common ancestor state of the enter/exit states. Then - * invoke the exit methods then the enter methods. + * Process the transitions including transitions in the enter/exit methods */ - StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); - invokeExitMethods(commonStateInfo); - int stateStackEnteringIndex = moveTempStateStackToStateStack(); - invokeEnterMethods(stateStackEnteringIndex); + while (true) { + if (mDbg) mSm.log("handleMessage: new destination call exit/enter"); + + /** + * Determine the states to exit and enter and return the + * common ancestor state of the enter/exit states. Then + * invoke the exit methods then the enter methods. + */ + StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); + invokeExitMethods(commonStateInfo); + int stateStackEnteringIndex = moveTempStateStackToStateStack(); + invokeEnterMethods(stateStackEnteringIndex); - /** - * Since we have transitioned to a new state we need to have - * any deferred messages moved to the front of the message queue - * so they will be processed before any other messages in the - * message queue. - */ - moveDeferredMessageAtFrontOfQueue(); + /** + * Since we have transitioned to a new state we need to have + * any deferred messages moved to the front of the message queue + * so they will be processed before any other messages in the + * message queue. + */ + moveDeferredMessageAtFrontOfQueue(); + + if (destState != mDestState) { + // A new mDestState so continue looping + destState = mDestState; + } else { + // No change in mDestState so we're done + break; + } + } + mDestState = null; } /** @@ -860,7 +920,7 @@ public class StateMachine { * Complete the construction of the state machine. */ private final void completeConstruction() { - if (mDbg) Log.d(TAG, "completeConstruction: E"); + if (mDbg) mSm.log("completeConstruction: E"); /** * Determine the maximum depth of the state hierarchy @@ -876,7 +936,7 @@ public class StateMachine { maxDepth = depth; } } - if (mDbg) Log.d(TAG, "completeConstruction: maxDepth=" + maxDepth); + if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth); mStateStack = new StateInfo[maxDepth]; mTempStateStack = new StateInfo[maxDepth]; @@ -885,18 +945,19 @@ public class StateMachine { /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */ sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)); - if (mDbg) Log.d(TAG, "completeConstruction: X"); + if (mDbg) mSm.log("completeConstruction: X"); } /** * Process the message. If the current state doesn't handle * it, call the states parent and so on. If it is never handled then * call the state machines unhandledMessage method. + * @return the state that processed the message */ - private final void processMsg(Message msg) { + private final State processMsg(Message msg) { StateInfo curStateInfo = mStateStack[mStateStackTopIndex]; if (mDbg) { - Log.d(TAG, "processMsg: " + curStateInfo.state.getName()); + mSm.log("processMsg: " + curStateInfo.state.getName()); } if (isQuit(msg)) { @@ -915,23 +976,11 @@ public class StateMachine { break; } if (mDbg) { - Log.d(TAG, "processMsg: " + curStateInfo.state.getName()); + mSm.log("processMsg: " + curStateInfo.state.getName()); } } - - /** - * Record that we processed the message - */ - if (mSm.recordLogRec(msg)) { - if (curStateInfo != null) { - State orgState = mStateStack[mStateStackTopIndex].state; - mLogRecords.add(msg, mSm.getLogRecString(msg), curStateInfo.state, - orgState); - } else { - mLogRecords.add(msg, mSm.getLogRecString(msg), null, null); - } - } - } + } + return (curStateInfo != null) ? curStateInfo.state : null; } /** @@ -942,7 +991,7 @@ public class StateMachine { while ((mStateStackTopIndex >= 0) && (mStateStack[mStateStackTopIndex] != commonStateInfo)) { State curState = mStateStack[mStateStackTopIndex].state; - if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName()); + if (mDbg) mSm.log("invokeExitMethods: " + curState.getName()); curState.exit(); mStateStack[mStateStackTopIndex].active = false; mStateStackTopIndex -= 1; @@ -954,7 +1003,7 @@ public class StateMachine { */ private final void invokeEnterMethods(int stateStackEnteringIndex) { for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { - if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName()); + if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); mStateStack[i].state.enter(); mStateStack[i].active = true; } @@ -972,7 +1021,7 @@ public class StateMachine { */ for (int i = mDeferredMessages.size() - 1; i >= 0; i-- ) { Message curMsg = mDeferredMessages.get(i); - if (mDbg) Log.d(TAG, "moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what); + if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what); sendMessageAtFrontOfQueue(curMsg); } mDeferredMessages.clear(); @@ -990,7 +1039,7 @@ public class StateMachine { int i = mTempStateStackCount - 1; int j = startingIndex; while (i >= 0) { - if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j); + if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j); mStateStack[j] = mTempStateStack[i]; j += 1; i -= 1; @@ -998,7 +1047,7 @@ public class StateMachine { mStateStackTopIndex = j - 1; if (mDbg) { - Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop=" + mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex + ",startingIndex=" + startingIndex + ",Top=" + mStateStack[mStateStackTopIndex].state.getName()); } @@ -1031,7 +1080,7 @@ public class StateMachine { } while ((curStateInfo != null) && !curStateInfo.active); if (mDbg) { - Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount=" + mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount=" + mTempStateStackCount + ",curStateInfo: " + curStateInfo); } return curStateInfo; @@ -1042,7 +1091,7 @@ public class StateMachine { */ private final void setupInitialStateStack() { if (mDbg) { - Log.d(TAG, "setupInitialStateStack: E mInitialState=" + mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName()); } @@ -1083,7 +1132,7 @@ public class StateMachine { */ private final StateInfo addState(State state, State parent) { if (mDbg) { - Log.d(TAG, "addStateInternal: E state=" + state.getName() + mSm.log("addStateInternal: E state=" + state.getName() + ",parent=" + ((parent == null) ? "" : parent.getName())); } StateInfo parentStateInfo = null; @@ -1108,7 +1157,7 @@ public class StateMachine { stateInfo.state = state; stateInfo.parentStateInfo = parentStateInfo; stateInfo.active = false; - if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo); + if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo); return stateInfo; } @@ -1128,19 +1177,19 @@ public class StateMachine { /** @see StateMachine#setInitialState(State) */ private final void setInitialState(State initialState) { - if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName()); + if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName()); mInitialState = initialState; } /** @see StateMachine#transitionTo(IState) */ private final void transitionTo(IState destState) { mDestState = (State) destState; - if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName()); + if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName()); } /** @see StateMachine#deferMessage(Message) */ private final void deferMessage(Message msg) { - if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what); + if (mDbg) mSm.log("deferMessage: msg=" + msg.what); /* Copy the "msg" to "newMsg" as "msg" will be recycled */ Message newMsg = obtainMessage(); @@ -1151,17 +1200,17 @@ public class StateMachine { /** @see StateMachine#quit() */ private final void quit() { - if (mDbg) Log.d(TAG, "quit:"); + if (mDbg) mSm.log("quit:"); sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); } /** @see StateMachine#quitNow() */ private final void quitNow() { - if (mDbg) Log.d(TAG, "abort:"); + if (mDbg) mSm.log("quitNow:"); sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); } - /** Validate that the message was sent by quit or abort. */ + /** Validate that the message was sent by quit or quitNow. */ private final boolean isQuit(Message msg) { return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj); } @@ -1224,20 +1273,6 @@ public class StateMachine { } /** - * @return current message - */ - protected final Message getCurrentMessage() { - return mSmHandler.getCurrentMessage(); - } - - /** - * @return current state - */ - protected final IState getCurrentState() { - return mSmHandler.getCurrentState(); - } - - /** * Add a new state to the state machine, parent will be null * @param state to add */ @@ -1256,6 +1291,26 @@ public class StateMachine { } /** + * @return current message + */ + protected final Message getCurrentMessage() { + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return null; + return smh.getCurrentMessage(); + } + + /** + * @return current state + */ + protected final IState getCurrentState() { + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return null; + return smh.getCurrentState(); + } + + /** * transition to destination state. Upon returning * from processMessage the current state's exit will * be executed and upon the next message arriving @@ -1303,7 +1358,7 @@ public class StateMachine { * @param msg that couldn't be handled. */ protected void unhandledMessage(Message msg) { - if (mSmHandler.mDbg) Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what); + if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what); } /** @@ -1347,43 +1402,69 @@ public class StateMachine { } /** + * Set to log only messages that cause a state transition + * + * @param enable {@code true} to enable, {@code false} to disable + */ + public final void setLogOnlyTransitions(boolean enable) { + mSmHandler.mLogRecords.setLogOnlyTransitions(enable); + } + + /** * @return number of log records */ public final int getLogRecSize() { - return mSmHandler.mLogRecords.size(); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return 0; + return smh.mLogRecords.size(); } /** * @return the total number of records processed */ public final int getLogRecCount() { - return mSmHandler.mLogRecords.count(); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return 0; + return smh.mLogRecords.count(); } /** - * @return a log record + * @return a log record, or null if index is out of range */ public final LogRec getLogRec(int index) { - return mSmHandler.mLogRecords.get(index); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return null; + return smh.mLogRecords.get(index); } /** - * Add the string to LogRecords. - * - * @param string + * @return a copy of LogRecs as a collection */ - protected void addLogRec(String string) { - mSmHandler.mLogRecords.add(null, string, null, null); + public final Collection<LogRec> copyLogRecs() { + Vector<LogRec> vlr = new Vector<LogRec>(); + SmHandler smh = mSmHandler; + if (smh != null) { + for (LogRec lr : smh.mLogRecords.mLogRecVector) { + vlr.add(lr); + } + } + return vlr; } /** - * Add the string and state to LogRecords + * Add the string to LogRecords. * * @param string - * @param state current state */ - protected void addLogRec(String string, State state) { - mSmHandler.mLogRecords.add(null, string, state, null); + protected void addLogRec(String string) { + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return; + smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(), + smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState); } /** @@ -1412,168 +1493,217 @@ public class StateMachine { } /** - * @return Handler + * @return Handler, maybe null if state machine has quit. */ public final Handler getHandler() { return mSmHandler; } /** - * Get a message and set Message.target = this. + * Get a message and set Message.target state machine handler. * - * @return message or null if SM has quit + * Note: The handler can be null if the state machine has quit, + * which means target will be null and may cause a AndroidRuntimeException + * in MessageQueue#enqueMessage if sent directly or if sent using + * StateMachine#sendMessage the message will just be ignored. + * + * @return A Message object from the global pool */ public final Message obtainMessage() { - if (mSmHandler == null) return null; - return Message.obtain(mSmHandler); } /** - * Get a message and set Message.target = this and what + * Get a message and set Message.target state machine handler, what. + * + * Note: The handler can be null if the state machine has quit, + * which means target will be null and may cause a AndroidRuntimeException + * in MessageQueue#enqueMessage if sent directly or if sent using + * StateMachine#sendMessage the message will just be ignored. * * @param what is the assigned to Message.what. - * @return message or null if SM has quit + * @return A Message object from the global pool */ public final Message obtainMessage(int what) { - if (mSmHandler == null) return null; - return Message.obtain(mSmHandler, what); } /** - * Get a message and set Message.target = this, + * Get a message and set Message.target state machine handler, * what and obj. * + * Note: The handler can be null if the state machine has quit, + * which means target will be null and may cause a AndroidRuntimeException + * in MessageQueue#enqueMessage if sent directly or if sent using + * StateMachine#sendMessage the message will just be ignored. + * * @param what is the assigned to Message.what. * @param obj is assigned to Message.obj. - * @return message or null if SM has quit + * @return A Message object from the global pool */ public final Message obtainMessage(int what, Object obj) { - if (mSmHandler == null) return null; - return Message.obtain(mSmHandler, what, obj); } /** - * Get a message and set Message.target = this, + * Get a message and set Message.target state machine handler, * what, arg1 and arg2 * + * Note: The handler can be null if the state machine has quit, + * which means target will be null and may cause a AndroidRuntimeException + * in MessageQueue#enqueMessage if sent directly or if sent using + * StateMachine#sendMessage the message will just be ignored. + * * @param what is assigned to Message.what * @param arg1 is assigned to Message.arg1 * @param arg2 is assigned to Message.arg2 - * @return A Message object from the global pool or null if - * SM has quit + * @return A Message object from the global pool */ public final Message obtainMessage(int what, int arg1, int arg2) { - if (mSmHandler == null) return null; - return Message.obtain(mSmHandler, what, arg1, arg2); } /** - * Get a message and set Message.target = this, + * Get a message and set Message.target state machine handler, * what, arg1, arg2 and obj * + * Note: The handler can be null if the state machine has quit, + * which means target will be null and may cause a AndroidRuntimeException + * in MessageQueue#enqueMessage if sent directly or if sent using + * StateMachine#sendMessage the message will just be ignored. + * * @param what is assigned to Message.what * @param arg1 is assigned to Message.arg1 * @param arg2 is assigned to Message.arg2 * @param obj is assigned to Message.obj - * @return A Message object from the global pool or null if - * SM has quit + * @return A Message object from the global pool */ public final Message obtainMessage(int what, int arg1, int arg2, Object obj) { - if (mSmHandler == null) return null; - return Message.obtain(mSmHandler, what, arg1, arg2, obj); } /** * Enqueue a message to this state machine. + * + * Message is ignored if state machine has quit. */ public final void sendMessage(int what) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.sendMessage(obtainMessage(what)); + smh.sendMessage(obtainMessage(what)); } /** * Enqueue a message to this state machine. + * + * Message is ignored if state machine has quit. */ public final void sendMessage(int what, Object obj) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.sendMessage(obtainMessage(what,obj)); + smh.sendMessage(obtainMessage(what,obj)); } /** * Enqueue a message to this state machine. + * + * Message is ignored if state machine has quit. */ public final void sendMessage(Message msg) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.sendMessage(msg); + smh.sendMessage(msg); } /** * Enqueue a message to this state machine after a delay. + * + * Message is ignored if state machine has quit. */ public final void sendMessageDelayed(int what, long delayMillis) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis); + smh.sendMessageDelayed(obtainMessage(what), delayMillis); } /** * Enqueue a message to this state machine after a delay. + * + * Message is ignored if state machine has quit. */ public final void sendMessageDelayed(int what, Object obj, long delayMillis) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis); + smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis); } /** * Enqueue a message to this state machine after a delay. + * + * Message is ignored if state machine has quit. */ public final void sendMessageDelayed(Message msg, long delayMillis) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.sendMessageDelayed(msg, delayMillis); + smh.sendMessageDelayed(msg, delayMillis); } /** * Enqueue a message to the front of the queue for this state machine. * Protected, may only be called by instances of StateMachine. + * + * Message is ignored if state machine has quit. */ protected final void sendMessageAtFrontOfQueue(int what, Object obj) { - mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj)); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return; + + smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj)); } /** * Enqueue a message to the front of the queue for this state machine. * Protected, may only be called by instances of StateMachine. + * + * Message is ignored if state machine has quit. */ protected final void sendMessageAtFrontOfQueue(int what) { - mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what)); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return; + + smh.sendMessageAtFrontOfQueue(obtainMessage(what)); } /** * Enqueue a message to the front of the queue for this state machine. * Protected, may only be called by instances of StateMachine. + * + * Message is ignored if state machine has quit. */ protected final void sendMessageAtFrontOfQueue(Message msg) { - mSmHandler.sendMessageAtFrontOfQueue(msg); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return; + + smh.sendMessageAtFrontOfQueue(msg); } /** @@ -1581,7 +1711,23 @@ public class StateMachine { * Protected, may only be called by instances of StateMachine. */ protected final void removeMessages(int what) { - mSmHandler.removeMessages(what); + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return; + + smh.removeMessages(what); + } + + /** + * Validate that the message was sent by + * {@link StateMachine#quit} or {@link StateMachine#quitNow}. + * */ + protected final boolean isQuit(Message msg) { + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return msg.what == SM_QUIT_CMD; + + return smh.isQuit(msg); } /** @@ -1589,9 +1735,10 @@ public class StateMachine { */ protected final void quit() { // mSmHandler can be null if the state machine is already stopped. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.quit(); + smh.quit(); } /** @@ -1599,9 +1746,10 @@ public class StateMachine { */ protected final void quitNow() { // mSmHandler can be null if the state machine is already stopped. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.quitNow(); + smh.quitNow(); } /** @@ -1609,9 +1757,10 @@ public class StateMachine { */ public boolean isDbg() { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return false; + SmHandler smh = mSmHandler; + if (smh == null) return false; - return mSmHandler.isDbg(); + return smh.isDbg(); } /** @@ -1621,9 +1770,10 @@ public class StateMachine { */ public void setDbg(boolean dbg) { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; - mSmHandler.setDbg(dbg); + smh.setDbg(dbg); } /** @@ -1631,10 +1781,11 @@ public class StateMachine { */ public void start() { // mSmHandler can be null if the state machine has quit. - if (mSmHandler == null) return; + SmHandler smh = mSmHandler; + if (smh == null) return; /** Send the complete construction message */ - mSmHandler.completeConstruction(); + smh.completeConstruction(); } /** @@ -1648,9 +1799,33 @@ public class StateMachine { pw.println(getName() + ":"); pw.println(" total records=" + getLogRecCount()); for (int i=0; i < getLogRecSize(); i++) { - pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString(this)); + pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString()); pw.flush(); } pw.println("curState=" + getCurrentState().getName()); } + + protected void log(String s) { + Log.d(mName, s); + } + + protected void logv(String s) { + Log.v(mName, s); + } + + protected void logi(String s) { + Log.i(mName, s); + } + + protected void logd(String s) { + Log.d(mName, s); + } + + protected void logw(String s) { + Log.w(mName, s); + } + + protected void loge(String s) { + Log.e(mName, s); + } } diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 6a09fe0..93f6cf6 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -123,18 +123,15 @@ public class XmlUtils return Integer.parseInt(nm.substring(index), base) * sign; } - public static final int - convertValueToUnsignedInt(String value, int defaultValue) - { - if (null == value) + public static int convertValueToUnsignedInt(String value, int defaultValue) { + if (null == value) { return defaultValue; + } return parseUnsignedIntAttribute(value); } - public static final int - parseUnsignedIntAttribute(CharSequence charSeq) - { + public static int parseUnsignedIntAttribute(CharSequence charSeq) { String value = charSeq.toString(); long bits; diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index 0cfe4fd..6120a09 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -84,6 +84,10 @@ public class ActionBarContainer extends FrameLayout { mBackground = bg; if (bg != null) { bg.setCallback(this); + if (mActionBarView != null) { + mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), + mActionBarView.getRight(), mActionBarView.getBottom()); + } } setWillNotDraw(mIsSplit ? mSplitBackground == null : mBackground == null && mStackedBackground == null); @@ -98,6 +102,10 @@ public class ActionBarContainer extends FrameLayout { mStackedBackground = bg; if (bg != null) { bg.setCallback(this); + if ((mIsStacked && mStackedBackground != null)) { + mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(), + mTabContainer.getRight(), mTabContainer.getBottom()); + } } setWillNotDraw(mIsSplit ? mSplitBackground == null : mBackground == null && mStackedBackground == null); @@ -112,6 +120,9 @@ public class ActionBarContainer extends FrameLayout { mSplitBackground = bg; if (bg != null) { bg.setCallback(this); + if (mIsSplit && mSplitBackground != null) { + mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + } } setWillNotDraw(mIsSplit ? mSplitBackground == null : mBackground == null && mStackedBackground == null); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 0f964b9..6bb7ac7 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -719,6 +719,7 @@ public class ActionBarView extends AbsActionBarView { if (mSpinner == null) { mSpinner = new Spinner(mContext, null, com.android.internal.R.attr.actionDropDownStyle); + mSpinner.setId(com.android.internal.R.id.action_bar_spinner); mListNavLayout = new LinearLayout(mContext, null, com.android.internal.R.attr.actionBarTabBarStyle); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java deleted file mode 100644 index af3fd42..0000000 --- a/core/java/com/android/internal/widget/DigitalClock.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import com.android.internal.R; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.database.ContentObserver; -import android.graphics.Typeface; -import android.os.Handler; -import android.provider.Settings; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.view.View; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import java.lang.ref.WeakReference; -import java.text.DateFormatSymbols; -import java.util.Calendar; - -/** - * Displays the time - */ -public class DigitalClock extends RelativeLayout { - - private static final String SYSTEM = "/system/fonts/"; - private static final String SYSTEM_FONT_TIME_BACKGROUND = SYSTEM + "AndroidClock.ttf"; - private static final String SYSTEM_FONT_TIME_FOREGROUND = SYSTEM + "AndroidClock_Highlight.ttf"; - private final static String M12 = "h:mm"; - private final static String M24 = "kk:mm"; - - private Calendar mCalendar; - private String mFormat; - private TextView mTimeDisplayBackground; - private TextView mTimeDisplayForeground; - private AmPm mAmPm; - private ContentObserver mFormatChangeObserver; - private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced - - /* called by system on minute ticks */ - private final Handler mHandler = new Handler(); - private BroadcastReceiver mIntentReceiver; - - private static final Typeface sBackgroundFont; - private static final Typeface sForegroundFont; - - static { - sBackgroundFont = Typeface.createFromFile(SYSTEM_FONT_TIME_BACKGROUND); - sForegroundFont = Typeface.createFromFile(SYSTEM_FONT_TIME_FOREGROUND); - } - - private static class TimeChangedReceiver extends BroadcastReceiver { - private WeakReference<DigitalClock> mClock; - private Context mContext; - - public TimeChangedReceiver(DigitalClock clock) { - mClock = new WeakReference<DigitalClock>(clock); - mContext = clock.getContext(); - } - - @Override - public void onReceive(Context context, Intent intent) { - // Post a runnable to avoid blocking the broadcast. - final boolean timezoneChanged = - intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED); - final DigitalClock clock = mClock.get(); - if (clock != null) { - clock.mHandler.post(new Runnable() { - public void run() { - if (timezoneChanged) { - clock.mCalendar = Calendar.getInstance(); - } - clock.updateTime(); - } - }); - } else { - try { - mContext.unregisterReceiver(this); - } catch (RuntimeException e) { - // Shouldn't happen - } - } - } - }; - - static class AmPm { - private TextView mAmPmTextView; - private String mAmString, mPmString; - - AmPm(View parent, Typeface tf) { - // No longer used, uncomment if we decide to use AM/PM indicator again - // mAmPmTextView = (TextView) parent.findViewById(R.id.am_pm); - if (mAmPmTextView != null && tf != null) { - mAmPmTextView.setTypeface(tf); - } - - String[] ampm = new DateFormatSymbols().getAmPmStrings(); - mAmString = ampm[0]; - mPmString = ampm[1]; - } - - void setShowAmPm(boolean show) { - if (mAmPmTextView != null) { - mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE); - } - } - - void setIsMorning(boolean isMorning) { - if (mAmPmTextView != null) { - mAmPmTextView.setText(isMorning ? mAmString : mPmString); - } - } - } - - private static class FormatChangeObserver extends ContentObserver { - private WeakReference<DigitalClock> mClock; - private Context mContext; - public FormatChangeObserver(DigitalClock clock) { - super(new Handler()); - mClock = new WeakReference<DigitalClock>(clock); - mContext = clock.getContext(); - } - @Override - public void onChange(boolean selfChange) { - DigitalClock digitalClock = mClock.get(); - if (digitalClock != null) { - digitalClock.setDateFormat(); - digitalClock.updateTime(); - } else { - try { - mContext.getContentResolver().unregisterContentObserver(this); - } catch (RuntimeException e) { - // Shouldn't happen - } - } - } - } - - public DigitalClock(Context context) { - this(context, null); - } - - public DigitalClock(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - /* The time display consists of two tones. That's why we have two overlapping text views. */ - mTimeDisplayBackground = (TextView) findViewById(R.id.timeDisplayBackground); - mTimeDisplayBackground.setTypeface(sBackgroundFont); - mTimeDisplayBackground.setVisibility(View.INVISIBLE); - - mTimeDisplayForeground = (TextView) findViewById(R.id.timeDisplayForeground); - mTimeDisplayForeground.setTypeface(sForegroundFont); - mAmPm = new AmPm(this, null); - mCalendar = Calendar.getInstance(); - - setDateFormat(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - mAttached++; - - /* monitor time ticks, time changed, timezone */ - if (mIntentReceiver == null) { - mIntentReceiver = new TimeChangedReceiver(this); - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_TIME_TICK); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - mContext.registerReceiver(mIntentReceiver, filter); - } - - /* monitor 12/24-hour display preference */ - if (mFormatChangeObserver == null) { - mFormatChangeObserver = new FormatChangeObserver(this); - mContext.getContentResolver().registerContentObserver( - Settings.System.CONTENT_URI, true, mFormatChangeObserver); - } - - updateTime(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - mAttached--; - - if (mIntentReceiver != null) { - mContext.unregisterReceiver(mIntentReceiver); - } - if (mFormatChangeObserver != null) { - mContext.getContentResolver().unregisterContentObserver( - mFormatChangeObserver); - } - - mFormatChangeObserver = null; - mIntentReceiver = null; - } - - void updateTime(Calendar c) { - mCalendar = c; - updateTime(); - } - - public void updateTime() { - mCalendar.setTimeInMillis(System.currentTimeMillis()); - - CharSequence newTime = DateFormat.format(mFormat, mCalendar); - mTimeDisplayBackground.setText(newTime); - mTimeDisplayForeground.setText(newTime); - mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0); - } - - private void setDateFormat() { - mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) - ? M24 : M12; - mAmPm.setShowAmPm(mFormat.equals(M12)); - } -} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 907b52a..555c7c2 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -123,14 +123,14 @@ public class LockPatternUtils { */ public static final int ID_DEFAULT_STATUS_WIDGET = -2; - protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; - protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; - protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; + public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; + public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; + public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; - protected final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; - protected final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; - protected final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; + public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; + public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; + public final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK = "lockscreen.biometric_weak_fallback"; public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY @@ -138,7 +138,7 @@ public class LockPatternUtils { public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS = "lockscreen.power_button_instantly_locks"; - protected final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; + public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; private final Context mContext; private final ContentResolver mContentResolver; diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java deleted file mode 100644 index 4ecbd16..0000000 --- a/core/java/com/android/internal/widget/LockSettingsService.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.os.Binder; -import android.os.Environment; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.provider.Settings; -import android.provider.Settings.Secure; -import android.text.TextUtils; -import android.util.Slog; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.Arrays; - -/** - * Keeps the lock pattern/password data and related settings for each user. - * Used by LockPatternUtils. Needs to be a service because Settings app also needs - * to be able to save lockscreen information for secondary users. - * @hide - */ -public class LockSettingsService extends ILockSettings.Stub { - - private final DatabaseHelper mOpenHelper; - private static final String TAG = "LockSettingsService"; - - private static final String TABLE = "locksettings"; - private static final String COLUMN_KEY = "name"; - private static final String COLUMN_USERID = "user"; - private static final String COLUMN_VALUE = "value"; - - private static final String[] COLUMNS_FOR_QUERY = { - COLUMN_VALUE - }; - - private static final String SYSTEM_DIRECTORY = "/system/"; - private static final String LOCK_PATTERN_FILE = "gesture.key"; - private static final String LOCK_PASSWORD_FILE = "password.key"; - - private final Context mContext; - - public LockSettingsService(Context context) { - mContext = context; - // Open the database - mOpenHelper = new DatabaseHelper(mContext); - } - - public void systemReady() { - migrateOldData(); - } - - private void migrateOldData() { - try { - if (getString("migrated", null, 0) != null) { - // Already migrated - return; - } - - final ContentResolver cr = mContext.getContentResolver(); - for (String validSetting : VALID_SETTINGS) { - String value = Settings.Secure.getString(cr, validSetting); - if (value != null) { - setString(validSetting, value, 0); - } - } - // No need to move the password / pattern files. They're already in the right place. - setString("migrated", "true", 0); - Slog.i(TAG, "Migrated lock settings to new location"); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to migrate old data"); - } - } - - private static final void checkWritePermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { - throw new SecurityException("uid=" + callingUid - + " not authorized to write lock settings"); - } - } - - private static final void checkPasswordReadPermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read lock password"); - } - } - - private static final void checkReadPermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID - && UserHandle.getUserId(callingUid) != userId) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read settings of user " + userId); - } - } - - @Override - public void setBoolean(String key, boolean value, int userId) throws RemoteException { - checkWritePermission(userId); - - writeToDb(key, value ? "1" : "0", userId); - } - - @Override - public void setLong(String key, long value, int userId) throws RemoteException { - checkWritePermission(userId); - - writeToDb(key, Long.toString(value), userId); - } - - @Override - public void setString(String key, String value, int userId) throws RemoteException { - checkWritePermission(userId); - - writeToDb(key, value, userId); - } - - @Override - public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); - - String value = readFromDb(key, null, userId); - return TextUtils.isEmpty(value) ? - defaultValue : (value.equals("1") || value.equals("true")); - } - - @Override - public long getLong(String key, long defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); - - String value = readFromDb(key, null, userId); - return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); - } - - @Override - public String getString(String key, String defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); - - return readFromDb(key, defaultValue, userId); - } - - private String getLockPatternFilename(int userId) { - String dataSystemDirectory = - android.os.Environment.getDataDirectory().getAbsolutePath() + - SYSTEM_DIRECTORY; - if (userId == 0) { - // Leave it in the same place for user 0 - return dataSystemDirectory + LOCK_PATTERN_FILE; - } else { - return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE) - .getAbsolutePath(); - } - } - - private String getLockPasswordFilename(int userId) { - String dataSystemDirectory = - android.os.Environment.getDataDirectory().getAbsolutePath() + - SYSTEM_DIRECTORY; - if (userId == 0) { - // Leave it in the same place for user 0 - return dataSystemDirectory + LOCK_PASSWORD_FILE; - } else { - return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE) - .getAbsolutePath(); - } - } - - @Override - public boolean havePassword(int userId) throws RemoteException { - // Do we need a permissions check here? - - return new File(getLockPasswordFilename(userId)).length() > 0; - } - - @Override - public boolean havePattern(int userId) throws RemoteException { - // Do we need a permissions check here? - - return new File(getLockPatternFilename(userId)).length() > 0; - } - - @Override - public void setLockPattern(byte[] hash, int userId) throws RemoteException { - checkWritePermission(userId); - - writeFile(getLockPatternFilename(userId), hash); - } - - @Override - public boolean checkPattern(byte[] hash, int userId) throws RemoteException { - checkPasswordReadPermission(userId); - try { - // Read all the bytes from the file - RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r"); - final byte[] stored = new byte[(int) raf.length()]; - int got = raf.read(stored, 0, stored.length); - raf.close(); - if (got <= 0) { - return true; - } - // Compare the hash from the file with the entered pattern's hash - return Arrays.equals(stored, hash); - } catch (FileNotFoundException fnfe) { - Slog.e(TAG, "Cannot read file " + fnfe); - return true; - } catch (IOException ioe) { - Slog.e(TAG, "Cannot read file " + ioe); - return true; - } - } - - @Override - public void setLockPassword(byte[] hash, int userId) throws RemoteException { - checkWritePermission(userId); - - writeFile(getLockPasswordFilename(userId), hash); - } - - @Override - public boolean checkPassword(byte[] hash, int userId) throws RemoteException { - checkPasswordReadPermission(userId); - - try { - // Read all the bytes from the file - RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r"); - final byte[] stored = new byte[(int) raf.length()]; - int got = raf.read(stored, 0, stored.length); - raf.close(); - if (got <= 0) { - return true; - } - // Compare the hash from the file with the entered password's hash - return Arrays.equals(stored, hash); - } catch (FileNotFoundException fnfe) { - Slog.e(TAG, "Cannot read file " + fnfe); - return true; - } catch (IOException ioe) { - Slog.e(TAG, "Cannot read file " + ioe); - return true; - } - } - - @Override - public void removeUser(int userId) { - checkWritePermission(userId); - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - try { - File file = new File(getLockPasswordFilename(userId)); - if (file.exists()) { - file.delete(); - } - file = new File(getLockPatternFilename(userId)); - if (file.exists()) { - file.delete(); - } - - db.beginTransaction(); - db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - private void writeFile(String name, byte[] hash) { - try { - // Write the hash to file - RandomAccessFile raf = new RandomAccessFile(name, "rw"); - // Truncate the file if pattern is null, to clear the lock - if (hash == null || hash.length == 0) { - raf.setLength(0); - } else { - raf.write(hash, 0, hash.length); - } - raf.close(); - } catch (IOException ioe) { - Slog.e(TAG, "Error writing to file " + ioe); - } - } - - private void writeToDb(String key, String value, int userId) { - writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId); - } - - private void writeToDb(SQLiteDatabase db, String key, String value, int userId) { - ContentValues cv = new ContentValues(); - cv.put(COLUMN_KEY, key); - cv.put(COLUMN_USERID, userId); - cv.put(COLUMN_VALUE, value); - - db.beginTransaction(); - try { - db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", - new String[] {key, Integer.toString(userId)}); - db.insert(TABLE, null, cv); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - private String readFromDb(String key, String defaultValue, int userId) { - Cursor cursor; - String result = defaultValue; - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, - COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", - new String[] { Integer.toString(userId), key }, - null, null, null)) != null) { - if (cursor.moveToFirst()) { - result = cursor.getString(0); - } - cursor.close(); - } - return result; - } - - class DatabaseHelper extends SQLiteOpenHelper { - private static final String TAG = "LockSettingsDB"; - private static final String DATABASE_NAME = "locksettings.db"; - - private static final int DATABASE_VERSION = 1; - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - setWriteAheadLoggingEnabled(true); - } - - private void createTable(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE + " (" + - "_id INTEGER PRIMARY KEY AUTOINCREMENT," + - COLUMN_KEY + " TEXT," + - COLUMN_USERID + " INTEGER," + - COLUMN_VALUE + " TEXT" + - ");"); - } - - @Override - public void onCreate(SQLiteDatabase db) { - createTable(db); - initializeDefaults(db); - } - - private void initializeDefaults(SQLiteDatabase db) { - // Get the lockscreen default from a system property, if available - boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default", - false); - if (lockScreenDisable) { - writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { - // Nothing yet - } - } - - private static final String[] VALID_SETTINGS = new String[] { - LockPatternUtils.LOCKOUT_PERMANENT_KEY, - LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, - LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, - LockPatternUtils.PASSWORD_TYPE_KEY, - LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, - LockPatternUtils.LOCK_PASSWORD_SALT_KEY, - LockPatternUtils.DISABLE_LOCKSCREEN_KEY, - LockPatternUtils.LOCKSCREEN_OPTIONS, - LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, - LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY, - LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, - LockPatternUtils.PASSWORD_HISTORY_KEY, - Secure.LOCK_PATTERN_ENABLED, - Secure.LOCK_BIOMETRIC_WEAK_FLAGS, - Secure.LOCK_PATTERN_VISIBLE, - Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED - }; -} diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java index 273f6fe..ba113a3 100644 --- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java +++ b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java @@ -225,13 +225,11 @@ public class SizeAdaptiveLayout extends ViewGroup { if (unboundedView != null) { tallestView = unboundedView; } - if (heightMode == MeasureSpec.UNSPECIFIED) { - return tallestView; - } - if (heightSize > tallestViewSize) { + if (heightMode == MeasureSpec.UNSPECIFIED || heightSize > tallestViewSize) { return tallestView; + } else { + return smallestView; } - return smallestView; } @Override @@ -272,10 +270,10 @@ public class SizeAdaptiveLayout extends ViewGroup { final int childWidth = mActiveChild.getMeasuredWidth(); final int childHeight = mActiveChild.getMeasuredHeight(); // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive - mActiveChild.layout(0, 0, 0 + childWidth, 0 + childHeight); + mActiveChild.layout(0, 0, childWidth, childHeight); if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop); - mModestyPanel.layout(0, mModestyPanelTop, 0 + childWidth, mModestyPanelTop + childHeight); + mModestyPanel.layout(0, mModestyPanelTop, childWidth, mModestyPanelTop + childHeight); } @Override diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 9b95be1..c6b7631 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -42,6 +42,7 @@ LOCAL_SRC_FILES:= \ android_emoji_EmojiFactory.cpp \ android_view_DisplayEventReceiver.cpp \ android_view_Surface.cpp \ + android_view_SurfaceControl.cpp \ android_view_SurfaceSession.cpp \ android_view_TextureView.cpp \ android_view_InputChannel.cpp \ @@ -91,7 +92,7 @@ LOCAL_SRC_FILES:= \ android/graphics/DrawFilter.cpp \ android/graphics/CreateJavaOutputStreamAdaptor.cpp \ android/graphics/Graphics.cpp \ - android/graphics/HarfbuzzSkia.cpp \ + android/graphics/HarfBuzzNGFaceSkia.cpp \ android/graphics/Interpolator.cpp \ android/graphics/LayerRasterizer.cpp \ android/graphics/MaskFilter.cpp \ @@ -145,7 +146,8 @@ LOCAL_SRC_FILES:= \ android_app_backup_FullBackup.cpp \ android_content_res_ObbScanner.cpp \ android_content_res_Configuration.cpp \ - android_animation_PropertyValuesHolder.cpp + android_animation_PropertyValuesHolder.cpp \ + com_android_internal_net_NetworkStatsFactory.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ @@ -155,7 +157,7 @@ LOCAL_C_INCLUDES += \ $(call include-path-for, bluedroid) \ $(call include-path-for, libhardware)/hardware \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ - $(TOP)/frameworks/av/include \ + $(TOP)/frameworks/av/include \ external/skia/include/core \ external/skia/include/effects \ external/skia/include/images \ @@ -170,8 +172,7 @@ LOCAL_C_INCLUDES += \ external/icu4c/i18n \ external/icu4c/common \ external/jpeg \ - external/harfbuzz/contrib \ - external/harfbuzz/src \ + external/harfbuzz_ng/src \ external/zlib \ frameworks/opt/emoji \ libcore/include @@ -203,11 +204,10 @@ LOCAL_SHARED_LIBRARIES := \ libicuuc \ libicui18n \ libmedia \ - libmedia_native \ libwpa_client \ libjpeg \ libusbhost \ - libharfbuzz \ + libharfbuzz_ng \ libz ifeq ($(USE_OPENGL_RENDERER),true) diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 94324f8..86d3cb6 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -47,8 +47,6 @@ using namespace android; -extern void register_BindTest(); - extern int register_android_os_Binder(JNIEnv* env); extern int register_android_os_Process(JNIEnv* env); extern int register_android_graphics_Bitmap(JNIEnv*); @@ -121,6 +119,7 @@ extern int register_android_view_GLES20DisplayList(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); extern int register_android_view_HardwareRenderer(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); +extern int register_android_view_SurfaceControl(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); extern int register_android_view_TextureView(JNIEnv* env); extern int register_android_database_CursorWindow(JNIEnv* env); @@ -173,6 +172,7 @@ extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); +extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -1112,6 +1112,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_GLES20Canvas), REG_JNI(register_android_view_HardwareRenderer), REG_JNI(register_android_view_Surface), + REG_JNI(register_android_view_SurfaceControl), REG_JNI(register_android_view_SurfaceSession), REG_JNI(register_android_view_TextureView), REG_JNI(register_com_google_android_gles_jni_EGLImpl), @@ -1204,6 +1205,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), + REG_JNI(register_com_android_internal_net_NetworkStatsFactory), }; /* diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 8823328..b7fdecf 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -210,19 +210,28 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, JavaPixelAllocator javaAllocator(env); SkBitmap* bitmap; + bool useExistingBitmap = false; if (javaBitmap == NULL) { bitmap = new SkBitmap; } else { if (sampleSize != 1) { return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1"); } + bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); - // config of supplied bitmap overrules config set in options - prefConfig = bitmap->getConfig(); + // only reuse the provided bitmap if it is immutable + if (!bitmap->isImmutable()) { + useExistingBitmap = true; + // config of supplied bitmap overrules config set in options + prefConfig = bitmap->getConfig(); + } else { + ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); + bitmap = new SkBitmap; + } } SkAutoTDelete<SkImageDecoder> add(decoder); - SkAutoTDelete<SkBitmap> adb(bitmap, javaBitmap == NULL); + SkAutoTDelete<SkBitmap> adb(bitmap, !useExistingBitmap); decoder->setPeeker(&peeker); if (!isPurgeable) { @@ -375,7 +384,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, return nullObjectReturn("Got null SkPixelRef"); } - if (!isMutable) { + if (!isMutable && !useExistingBitmap) { // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); } @@ -383,7 +392,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, // detach bitmap from its autodeleter, since we want to own it now adb.detach(); - if (javaBitmap != NULL) { + if (useExistingBitmap) { // If a java bitmap was passed in for reuse, pass it back return javaBitmap; } diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 5d6f738..886eb6e 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -733,37 +733,37 @@ public: } - static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas, + static void drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); - drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint); + drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, paint); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } - static void drawText__StringIIFFIPaint(JNIEnv* env, jobject, + static void drawText__StringIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, jstring text, int start, int end, - jfloat x, jfloat y, int flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); - drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint); + drawTextWithGlyphs(canvas, textArray, start, end, x, y, paint); env->ReleaseStringChars(text, textArray); } static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray, int start, int end, - jfloat x, jfloat y, int flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { jint count = end - start; - drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, flags, paint); + drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, paint); } static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray, int start, int count, int contextCount, - jfloat x, jfloat y, int flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - textArray, start, count, contextCount, flags); + textArray, start, count, contextCount); if (value == NULL) { return; } @@ -774,7 +774,7 @@ public: x -= value->getTotalAdvance(); } paint->setTextAlign(SkPaint::kLeft_Align); - doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint); + doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, paint); doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint); paint->setTextAlign(align); } @@ -816,14 +816,8 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l } } - static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { - // Beware: this needs Glyph encoding (already done on the Paint constructor) - canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint); - } - static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray, - int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { + int index, int count, jfloat x, jfloat y, SkPaint* paint) { SkPoint* posPtr = new SkPoint[count]; for (int indx = 0; indx < count; indx++) { posPtr[indx].fX = SkFloatToScalar(x + posArray[indx * 2]); @@ -833,27 +827,27 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l delete[] posPtr; } - static void drawTextRun___CIIIIFFIPaint( + static void drawTextRun___CIIIIFFPaint( JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, int count, int contextIndex, int contextCount, - jfloat x, jfloat y, int dirFlags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { jchar* chars = env->GetCharArrayElements(text, NULL); drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex, - count, contextCount, x, y, dirFlags, paint); + count, contextCount, x, y, paint); env->ReleaseCharArrayElements(text, chars, JNI_ABORT); } - static void drawTextRun__StringIIIIFFIPaint( + static void drawTextRun__StringIIIIFFPaint( JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, jint start, jint end, jint contextStart, jint contextEnd, - jfloat x, jfloat y, jint dirFlags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { jint count = end - start; jint contextCount = contextEnd - contextStart; const jchar* chars = env->GetStringChars(text, NULL); drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart, - count, contextCount, x, y, dirFlags, paint); + count, contextCount, x, y, paint); env->ReleaseStringChars(text, chars); } @@ -916,21 +910,19 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, int count, - SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) { + SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); - TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset, - path, canvas); + TextLayout::drawTextOnPath(paint, textArray + index, count, hOffset, vOffset, path, canvas); env->ReleaseCharArrayElements(text, textArray, 0); } static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, jstring text, SkPath* path, - jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) { + jfloat hOffset, jfloat vOffset, SkPaint* paint) { const jchar* text_ = env->GetStringChars(text, NULL); int count = env->GetStringLength(text); - TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset, - path, canvas); + TextLayout::drawTextOnPath(paint, text_, count, hOffset, vOffset, path, canvas); env->ReleaseStringChars(text, text_); } @@ -1033,21 +1025,21 @@ static JNINativeMethod gCanvasMethods[] = { (void*)SkCanvasGlue::drawBitmapMesh}, {"nativeDrawVertices", "(III[FI[FI[II[SIII)V", (void*)SkCanvasGlue::drawVertices}, - {"native_drawText","(I[CIIFFII)V", - (void*) SkCanvasGlue::drawText___CIIFFIPaint}, - {"native_drawText","(ILjava/lang/String;IIFFII)V", - (void*) SkCanvasGlue::drawText__StringIIFFIPaint}, - {"native_drawTextRun","(I[CIIIIFFII)V", - (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint}, - {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V", - (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaint}, + {"native_drawText","(I[CIIFFI)V", + (void*) SkCanvasGlue::drawText___CIIFFPaint}, + {"native_drawText","(ILjava/lang/String;IIFFI)V", + (void*) SkCanvasGlue::drawText__StringIIFFPaint}, + {"native_drawTextRun","(I[CIIIIFFI)V", + (void*) SkCanvasGlue::drawTextRun___CIIIIFFPaint}, + {"native_drawTextRun","(ILjava/lang/String;IIIIFFI)V", + (void*) SkCanvasGlue::drawTextRun__StringIIIIFFPaint}, {"native_drawPosText","(I[CII[FI)V", (void*) SkCanvasGlue::drawPosText___CII_FPaint}, {"native_drawPosText","(ILjava/lang/String;[FI)V", (void*) SkCanvasGlue::drawPosText__String_FPaint}, - {"native_drawTextOnPath","(I[CIIIFFII)V", + {"native_drawTextOnPath","(I[CIIIFFI)V", (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint}, - {"native_drawTextOnPath","(ILjava/lang/String;IFFII)V", + {"native_drawTextOnPath","(ILjava/lang/String;IFFI)V", (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint}, {"native_drawPicture", "(II)V", (void*) SkCanvasGlue::drawPicture}, diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp new file mode 100644 index 0000000..ce31c5b --- /dev/null +++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TextLayoutCache" + +#include "HarfBuzzNGFaceSkia.h" + +#include <cutils/log.h> +#include <SkFontHost.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkPoint.h> +#include <SkRect.h> +#include <SkUtils.h> + +#include <hb.h> + +namespace android { + +// Our implementation of the callbacks which Harfbuzz requires by using Skia +// calls. See the Harfbuzz source for references about what these callbacks do. + +struct HarfBuzzFontData { + HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { } + SkPaint* m_paint; +}; + +static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents) +{ + ALOG_ASSERT(codepoint <= 0xFFFF); + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkScalar skWidth; + SkRect skBounds; + uint16_t glyph = codepoint; + + paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds); +#if DEBUG_GLYPHS + ALOGD("returned glyph for %i: width = %f", codepoint, skWidth); +#endif + if (width) + *width = SkScalarToHBFixed(skWidth); + if (extents) { + // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up. + extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft); + extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop); + extents->width = SkScalarToHBFixed(skBounds.width()); + extents->height = SkScalarToHBFixed(-skBounds.height()); + } +} + +static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData) +{ + HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData); + + if (unicode > 0x10ffff) { + unicode = 0xfffd; + } + SkPaint* paint = hbFontData->m_paint; + // It would be better to use kUTF32_TextEncoding directly + paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); + uint16_t glyph16; + uint16_t unichar[2]; + size_t size = SkUTF16_FromUnichar(unicode, unichar); + paint->textToGlyphs(unichar, size * sizeof(*unichar), &glyph16); + *glyph = glyph16; + return !!*glyph; +} + +static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData) +{ + HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData); + hb_position_t advance = 0; + + SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0); + return advance; +} + +static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData) +{ + // Just return true, following the way that Harfbuzz-FreeType + // implementation does. + return true; +} + +static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData) +{ + HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData); + + SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents); + return true; +} + +static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs() +{ + static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0; + + // We don't set callback functions which we can't support. + // Harfbuzz will use the fallback implementation if they aren't set. + if (!harfbuzzSkiaFontFuncs) { + harfbuzzSkiaFontFuncs = hb_font_funcs_create(); + hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0); + hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0); + hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0); + hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0); + hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs); + } + return harfbuzzSkiaFontFuncs; +} + +hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData) +{ + SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData); + SkFontID uniqueID = typeface->uniqueID(); + + const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag); + if (!tableSize) + return 0; + + char* buffer = reinterpret_cast<char*>(malloc(tableSize)); + if (!buffer) + return 0; + size_t actualSize = SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer); + if (tableSize != actualSize) { + free(buffer); + return 0; + } + + return hb_blob_create(const_cast<char*>(buffer), tableSize, + HB_MEMORY_MODE_WRITABLE, buffer, free); +} + +static void destroyHarfBuzzFontData(void* data) { + delete (HarfBuzzFontData*)data; +} + +hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) { + hb_font_t* font = hb_font_create(face); + + // Note: this needs to be reworked when we do subpixels + int x_ppem = floor(sizeX + 0.5); + int y_ppem = floor(sizeY + 0.5); + hb_font_set_ppem(font, x_ppem, y_ppem); + hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY)); + + HarfBuzzFontData* data = new HarfBuzzFontData(paint); + hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData); + + return font; +} + +} // namespace android diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h index 2772f4d..7b71ecc 100644 --- a/core/jni/android/graphics/HarfbuzzSkia.h +++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h @@ -24,31 +24,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HarfbuzzSkia_h -#define HarfbuzzSkia_h +#ifndef HarfBuzzNGFaceSkia_h +#define HarfBuzzNGFaceSkia_h -#include "SkScalar.h" -#include "SkTypeface.h" -#include "SkPaint.h" +#include <SkScalar.h> +#include <SkPaint.h> -extern "C" { -#include "harfbuzz-shaper.h" -} +#include <hb.h> namespace android { -static inline float HBFixedToFloat(HB_Fixed v) { - // Harfbuzz uses 26.6 fixed point values for pixel offsets - return v * (1.0f / 64); +static inline float +HBFixedToFloat (hb_position_t v) +{ + return scalbnf (v, -8); +} + +static inline hb_position_t +HBFloatToFixed (float v) +{ + return scalbnf (v, +8); } -static inline HB_Fixed SkScalarToHBFixed(SkScalar value) { - // HB_Fixed is a 26.6 fixed point format. - return SkScalarToFloat(value) * 64.0f; +static inline hb_position_t SkScalarToHBFixed(SkScalar value) { + return HBFloatToFixed(SkScalarToFloat(value)); } -HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len); -extern const HB_FontClass harfbuzzSkiaClass; +hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData); + +hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY); } // namespace android diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp deleted file mode 100644 index 7e08379..0000000 --- a/core/jni/android/graphics/HarfbuzzSkia.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2011, The Android Open Source Project - * Copyright 2011, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LOG_TAG "HarfbuzzSkia" - -#include "HarfbuzzSkia.h" - -#include "SkFontHost.h" - -#include "SkPaint.h" -#include "SkPath.h" -#include "SkPoint.h" -#include "SkRect.h" -#include "SkTypeface.h" - -#include <utils/Log.h> - -extern "C" { -#include "harfbuzz-shaper.h" -} - -// This file implements the callbacks which Harfbuzz requires by using Skia -// calls. See the Harfbuzz source for references about what these callbacks do. - -namespace android { - -static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, - HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL) -{ - SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); - paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); - - uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs); - int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs); - - // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our - // |glyphs| array needs to be converted. - for (int i = numGlyphs - 1; i >= 0; --i) { - glyphs[i] = skiaGlyphs[i]; - } - - *glyphsSize = numGlyphs; - return 1; -} - -static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, - HB_Fixed* advances, int flags) -{ - SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); - paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); - - uint16_t* glyphs16 = new uint16_t[numGlyphs]; - if (!glyphs16) - return; - for (unsigned i = 0; i < numGlyphs; ++i) - glyphs16[i] = glyphs[i]; - SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances); - paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances); - - // The |advances| values which Skia outputs are SkScalars, which are floats - // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. - // These two formats are both 32-bits long. - for (unsigned i = 0; i < numGlyphs; ++i) { - advances[i] = SkScalarToHBFixed(scalarAdvances[i]); -#if DEBUG_ADVANCES - ALOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]); -#endif - } - delete glyphs16; -} - -static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length) -{ - SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); - paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); - - uint16_t* glyphs16 = new uint16_t[length]; - int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16); - - bool result = true; - for (int i = 0; i < numGlyphs; ++i) { - if (!glyphs16[i]) { - result = false; - break; - } - } - delete glyphs16; - return result; -} - -static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, - HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints) -{ - if (flags & HB_ShaperFlag_UseDesignMetrics) - // This is requesting pre-hinted positions. We can't support this. - return HB_Err_Invalid_Argument; - - SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); - paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); - - uint16_t glyph16 = glyph; - SkPath path; - paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); - uint32_t numPoints = path.getPoints(0, 0); - if (point >= numPoints) - return HB_Err_Invalid_SubTable; - SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1))); - if (!points) - return HB_Err_Invalid_SubTable; - // Skia does let us get a single point from the path. - path.getPoints(points, point + 1); - *xPos = SkScalarToHBFixed(points[point].fX); - *yPos = SkScalarToHBFixed(points[point].fY); - *resultingNumPoints = numPoints; - delete points; - - return HB_Err_Ok; -} - -static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) -{ - SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); - paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); - - uint16_t glyph16 = glyph; - SkScalar width; - SkRect bounds; - paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); - - metrics->x = SkScalarToHBFixed(bounds.fLeft); - metrics->y = SkScalarToHBFixed(bounds.fTop); - metrics->width = SkScalarToHBFixed(bounds.width()); - metrics->height = SkScalarToHBFixed(bounds.height()); - - metrics->xOffset = SkScalarToHBFixed(width); - // We can't actually get the |y| correct because Skia doesn't export - // the vertical advance. However, nor we do ever render vertical text at - // the moment so it's unimportant. - metrics->yOffset = 0; -} - -static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) -{ - SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); - - SkPaint::FontMetrics skiaMetrics; - paint->getFontMetrics(&skiaMetrics); - - switch (metric) { - case HB_FontAscent: - return SkScalarToHBFixed(-skiaMetrics.fAscent); - // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. - default: - return 0; - } - return 0; -} - -const HB_FontClass harfbuzzSkiaClass = { - stringToGlyphs, - glyphsToAdvances, - canRender, - getOutlinePoint, - getGlyphMetrics, - getFontMetric, -}; - -HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len) -{ - SkTypeface* typeface = static_cast<SkTypeface*>(font); - - if (!typeface) { - ALOGD("Typeface cannot be null"); - return HB_Err_Invalid_Argument; - } - const size_t tableSize = SkFontHost::GetTableSize(typeface->uniqueID(), tag); - if (!tableSize) - return HB_Err_Invalid_Argument; - // If Harfbuzz specified a NULL buffer then it's asking for the size of the table. - if (!buffer) { - *len = tableSize; - return HB_Err_Ok; - } - - if (*len < tableSize) - return HB_Err_Invalid_Argument; - SkFontHost::GetTableData(typeface->uniqueID(), tag, 0, tableSize, buffer); - return HB_Err_Ok; -} - -} // namespace android diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 150caf3..07f55e0 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -401,7 +401,7 @@ public: jfloat result = 0; TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength, - paint->getFlags(), NULL /* dont need all advances */, &result); + NULL /* dont need all advances */, &result); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; @@ -426,7 +426,7 @@ public: jfloat width = 0; TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength, - paint->getFlags(), NULL /* dont need all advances */, &width); + NULL /* dont need all advances */, &width); env->ReleaseStringChars(text, textArray); return width; @@ -446,7 +446,7 @@ public: jfloat width = 0; TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength, - paint->getFlags(), NULL /* dont need all advances */, &width); + NULL /* dont need all advances */, &width); env->ReleaseStringChars(text, textArray); return width; @@ -473,7 +473,7 @@ public: jfloat* widthsArray = autoWidths.ptr(); TextLayout::getTextRunAdvances(paint, text, 0, count, count, - paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */); + widthsArray, NULL /* dont need totalAdvance */); return count; } @@ -494,48 +494,8 @@ public: return count; } - static int doTextGlyphs(JNIEnv* env, SkPaint* paint, const jchar* text, jint start, jint count, - jint contextCount, jint flags, jcharArray glyphs) { - NPE_CHECK_RETURN_ZERO(env, paint); - NPE_CHECK_RETURN_ZERO(env, text); - - if ((start | count | contextCount) < 0 || contextCount < count || !glyphs) { - doThrowAIOOBE(env); - return 0; - } - if (count == 0) { - return 0; - } - size_t glypthsLength = env->GetArrayLength(glyphs); - if ((size_t)count > glypthsLength) { - doThrowAIOOBE(env); - return 0; - } - - jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL); - - sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, start, count, contextCount, flags); - const jchar* shapedGlyphs = value->getGlyphs(); - size_t glyphsCount = value->getGlyphsCount(); - memcpy(glyphsArray, shapedGlyphs, sizeof(jchar) * glyphsCount); - - env->ReleaseCharArrayElements(glyphs, glyphsArray, JNI_ABORT); - return glyphsCount; - } - - static int getTextGlyphs__StringIIIII_C(JNIEnv* env, jobject clazz, SkPaint* paint, - jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, - jcharArray glyphs) { - const jchar* textArray = env->GetStringChars(text, NULL); - int count = doTextGlyphs(env, paint, textArray + contextStart, start - contextStart, - end - start, contextEnd - contextStart, flags, glyphs); - env->ReleaseStringChars(text, textArray); - return count; - } - static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text, - jint start, jint count, jint contextCount, jint flags, + jint start, jint count, jint contextCount, jfloatArray advances, jint advancesIndex) { NPE_CHECK_RETURN_ZERO(env, paint); NPE_CHECK_RETURN_ZERO(env, text); @@ -557,7 +517,7 @@ public: jfloat advancesArray[count]; jfloat totalAdvance = 0; - TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags, + TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, advancesArray, &totalAdvance); if (advances != NULL) { @@ -566,70 +526,32 @@ public: return totalAdvance; } - static jfloat doTextRunAdvancesICU(JNIEnv *env, SkPaint *paint, const jchar *text, - jint start, jint count, jint contextCount, jint flags, - jfloatArray advances, jint advancesIndex) { - NPE_CHECK_RETURN_ZERO(env, paint); - NPE_CHECK_RETURN_ZERO(env, text); - - if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) { - doThrowAIOOBE(env); - return 0; - } - if (count == 0) { - return 0; - } - if (advances) { - size_t advancesLength = env->GetArrayLength(advances); - if ((size_t)count > advancesLength) { - doThrowAIOOBE(env); - return 0; - } - } - - jfloat advancesArray[count]; - jfloat totalAdvance = 0; - - TextLayout::getTextRunAdvancesICU(paint, text, start, count, contextCount, flags, - advancesArray, totalAdvance); - - if (advances != NULL) { - env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray); - } - return totalAdvance; - } - - static float getTextRunAdvances___CIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint, + static float getTextRunAdvances___CIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, - jint flags, jfloatArray advances, jint advancesIndex, jint reserved) { + jfloatArray advances, jint advancesIndex) { jchar* textArray = env->GetCharArrayElements(text, NULL); - jfloat result = (reserved == 0) ? - doTextRunAdvances(env, paint, textArray + contextIndex, index - contextIndex, - count, contextCount, flags, advances, advancesIndex) : - doTextRunAdvancesICU(env, paint, textArray + contextIndex, index - contextIndex, - count, contextCount, flags, advances, advancesIndex); + jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex, + index - contextIndex, count, contextCount, advances, advancesIndex); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } - static float getTextRunAdvances__StringIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint, - jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, - jfloatArray advances, jint advancesIndex, jint reserved) { + static float getTextRunAdvances__StringIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + jstring text, jint start, jint end, jint contextStart, jint contextEnd, + jfloatArray advances, jint advancesIndex) { const jchar* textArray = env->GetStringChars(text, NULL); - jfloat result = (reserved == 0) ? - doTextRunAdvances(env, paint, textArray + contextStart, start - contextStart, - end - start, contextEnd - contextStart, flags, advances, advancesIndex) : - doTextRunAdvancesICU(env, paint, textArray + contextStart, start - contextStart, - end - start, contextEnd - contextStart, flags, advances, advancesIndex); + jfloat result = doTextRunAdvances(env, paint, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, + advances, advancesIndex); env->ReleaseStringChars(text, textArray); return result; } static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start, - jint count, jint flags, jint offset, jint opt) { + jint count, jint offset, jint opt) { jfloat scalarArray[count]; - TextLayout::getTextRunAdvances(paint, text, start, count, start + count, flags, + TextLayout::getTextRunAdvances(paint, text, start, count, start + count, scalarArray, NULL /* dont need totalAdvance */); jint pos = offset - start; @@ -670,39 +592,39 @@ public: } static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, - jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) { + jint contextStart, jint contextCount, jint offset, jint cursorOpt) { jchar* textArray = env->GetCharArrayElements(text, NULL); - jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags, + jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, offset, cursorOpt); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, - jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) { + jint contextStart, jint contextEnd, jint offset, jint cursorOpt) { const jchar* textArray = env->GetStringChars(text, NULL); jint result = doTextRunCursor(env, paint, textArray, contextStart, - contextEnd - contextStart, flags, offset, cursorOpt); + contextEnd - contextStart, offset, cursorOpt); env->ReleaseStringChars(text, textArray); return result; } static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count, - jint bidiFlags, jfloat x, jfloat y, SkPath *path) { - TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path); + jfloat x, jfloat y, SkPath *path) { + TextLayout::getTextPath(paint, text, count, x, y, path); } - static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags, + static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) { const jchar* textArray = env->GetCharArrayElements(text, NULL); - getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path); + getTextPath(env, paint, textArray + index, count, x, y, path); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); } - static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags, + static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) { const jchar* textArray = env->GetStringChars(text, NULL); - getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, y, path); + getTextPath(env, paint, textArray + start, end - start, x, y, path); env->ReleaseStringChars(text, textArray); } @@ -726,7 +648,7 @@ public: int count, float maxWidth, jfloatArray jmeasured, SkPaint::TextBufferDirection tbd) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint, - text, 0, count, count, paint.getFlags()); + text, 0, count, count); if (value == NULL) { return 0; } @@ -792,11 +714,13 @@ public: static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds, const SkPaint& paint) { - SkRect r{0,0,0,0}; + SkRect r; + r.set(0,0,0,0); + SkIRect ir; sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint, - text, 0, count, count, paint.getFlags()); + text, 0, count, count); if (value == NULL) { return; } @@ -884,19 +808,15 @@ static JNINativeMethod methods[] = { {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, - {"native_getTextRunAdvances","(I[CIIIII[FII)F", - (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FII}, - {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FII)F", - (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FII}, - - - {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I", - (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C}, - {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C}, - {"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I", + {"native_getTextRunAdvances","(I[CIIII[FI)F", + (void*) SkPaintGlue::getTextRunAdvances___CIIII_FI}, + {"native_getTextRunAdvances","(ILjava/lang/String;IIII[FI)F", + (void*) SkPaintGlue::getTextRunAdvances__StringIIII_FI}, + {"native_getTextRunCursor", "(I[CIIII)I", (void*) SkPaintGlue::getTextRunCursor___C}, + {"native_getTextRunCursor", "(ILjava/lang/String;IIII)I", (void*) SkPaintGlue::getTextRunCursor__String}, - {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C}, - {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String}, + {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C}, + {"native_getTextPath","(ILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String}, {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V", (void*) SkPaintGlue::getStringBounds }, {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V", diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp index 866d223..ab7cf46 100644 --- a/core/jni/android/graphics/Region.cpp +++ b/core/jni/android/graphics/Region.cpp @@ -255,7 +255,7 @@ static JNINativeMethod gRegionMethods[] = { // these are static methods { "nativeConstructor", "()I", (void*)Region_constructor }, { "nativeDestructor", "(I)V", (void*)Region_destructor }, - { "nativeSetRegion", "(II)Z", (void*)Region_setRegion }, + { "nativeSetRegion", "(II)V", (void*)Region_setRegion }, { "nativeSetRect", "(IIIII)Z", (void*)Region_setRect }, { "nativeSetPath", "(III)Z", (void*)Region_setPath }, { "nativeGetBounds", "(ILandroid/graphics/Rect;)Z", (void*)Region_getBounds }, diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index c48b974..296d9b2 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -18,8 +18,8 @@ #include <stdio.h> -#include <gui/SurfaceTexture.h> -#include <gui/SurfaceTextureClient.h> +#include <gui/GLConsumer.h> +#include <gui/Surface.h> #include <android_runtime/AndroidRuntime.h> @@ -40,6 +40,7 @@ const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTextur struct fields_t { jfieldID surfaceTexture; + jfieldID frameAvailableListener; jmethodID postEvent; }; static fields_t fields; @@ -47,10 +48,10 @@ static fields_t fields; // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp<SurfaceTexture>& surfaceTexture) + const sp<GLConsumer>& surfaceTexture) { - SurfaceTexture* const p = - (SurfaceTexture*)env->GetIntField(thiz, fields.surfaceTexture); + GLConsumer* const p = + (GLConsumer*)env->GetIntField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong(thiz); } @@ -60,19 +61,33 @@ static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, env->SetIntField(thiz, fields.surfaceTexture, (int)surfaceTexture.get()); } -sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) +static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, + jobject thiz, sp<GLConsumer::FrameAvailableListener> listener) { - sp<SurfaceTexture> surfaceTexture( - (SurfaceTexture*)env->GetIntField(thiz, fields.surfaceTexture)); - return surfaceTexture; + GLConsumer::FrameAvailableListener* const p = + (GLConsumer::FrameAvailableListener*) + env->GetIntField(thiz, fields.frameAvailableListener); + if (listener.get()) { + listener->incStrong(thiz); + } + if (p) { + p->decStrong(thiz); + } + env->SetIntField(thiz, fields.frameAvailableListener, (int)listener.get()); +} + +sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, + jobject thiz) +{ + return (GLConsumer*)env->GetIntField(thiz, fields.surfaceTexture); } sp<ANativeWindow> android_SurfaceTexture_getNativeWindow( JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); - sp<SurfaceTextureClient> surfaceTextureClient(surfaceTexture != NULL ? - new SurfaceTextureClient(surfaceTexture) : NULL); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? + new Surface(surfaceTexture->getBufferQueue()) : NULL); return surfaceTextureClient; } @@ -84,7 +99,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener +class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -168,6 +183,12 @@ static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz) ALOGE("can't find android/graphics/SurfaceTexture.%s", ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID); } + fields.frameAvailableListener = env->GetFieldID(clazz, + ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID, "I"); + if (fields.frameAvailableListener == NULL) { + ALOGE("can't find android/graphics/SurfaceTexture.%s", + ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID); + } fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;)V"); @@ -179,7 +200,7 @@ static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz) static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName, jobject weakThiz, jboolean allowSynchronous) { - sp<SurfaceTexture> surfaceTexture(new SurfaceTexture(texName, allowSynchronous)); + sp<GLConsumer> surfaceTexture(new GLConsumer(texName, allowSynchronous)); if (surfaceTexture == 0) { jniThrowException(env, OutOfResourcesException, "Unable to create native SurfaceTexture"); @@ -197,25 +218,27 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName, sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz, clazz)); surfaceTexture->setFrameAvailableListener(ctx); + SurfaceTexture_setFrameAvailableListener(env, thiz, ctx); } static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); + SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); } static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -227,20 +250,20 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -248,13 +271,13 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp index 2beedad..b77236c 100644 --- a/core/jni/android/graphics/TextLayout.cpp +++ b/core/jni/android/graphics/TextLayout.cpp @@ -28,33 +28,13 @@ namespace android { -// Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if -// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text -// looking for a character >= the first RTL character in unicode and assume we do if -// we find one. -bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) { - if (bidiFlags == kBidi_Force_LTR) { - return false; - } - if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) || - bidiFlags == kBidi_Force_RTL) { - return true; - } - for (int i = 0; i < len; ++i) { - if (text[i] >= UNICODE_FIRST_RTL_CHAR) { - return true; - } - } - return false; -} - // Draws or gets the path of a paragraph of text on a single line, running bidi and shaping. // This will draw if canvas is not null, otherwise path must be non-null and it will create // a path representing the text that would have been drawn. void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, - jint bidiFlags, jfloat x, jfloat y, SkPath *path) { + jfloat x, jfloat y, SkPath *path) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, len, len, bidiFlags); + text, 0, len, len); if (value == NULL) { return ; } @@ -65,10 +45,10 @@ void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, } void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, - jint count, jint contextCount, jint dirFlags, + jint count, jint contextCount, jfloat* resultAdvances, jfloat* resultTotalAdvance) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - chars, start, count, contextCount, dirFlags); + chars, start, count, contextCount); if (value == NULL) { return ; } @@ -80,29 +60,20 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta } } -void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, - jint count, jint contextCount, jint dirFlags, - jfloat* resultAdvances, jfloat& resultTotalAdvance) { - // Compute advances and return them - computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, - resultAdvances, &resultTotalAdvance); -} - void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len, - jint bidiFlags, jfloat x, jfloat y, SkPath *path) { - handleText(paint, text, len, bidiFlags, x, y, path); + jfloat x, jfloat y, SkPath *path) { + handleText(paint, text, len, x, y, path); } - void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, - int bidiFlags, jfloat hOffset, jfloat vOffset, + jfloat hOffset, jfloat vOffset, SkPath* path, SkCanvas* canvas) { SkScalar h_ = SkFloatToScalar(hOffset); SkScalar v_ = SkFloatToScalar(vOffset); sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, count, count, bidiFlags); + text, 0, count, count); if (value == NULL) { return; } @@ -111,73 +82,4 @@ void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint); } -void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags, - jfloat* outAdvances, jfloat* outTotalAdvance) { - SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); - jchar* buffer = tempBuffer.get(); - SkScalar* scalarArray = (SkScalar*)outAdvances; - - // this is where we'd call harfbuzz - // for now we just use ushape.c - size_t widths; - const jchar* text; - if (dirFlags & 0x1) { // rtl, call arabic shaping in case - UErrorCode status = U_ZERO_ERROR; - // Use fixed length since we need to keep start and count valid - u_shapeArabic(chars, contextCount, buffer, contextCount, - U_SHAPE_LENGTH_FIXED_SPACES_NEAR | - U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | - U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); - // we shouldn't fail unless there's an out of memory condition, - // in which case we're hosed anyway - for (int i = start, e = i + count; i < e; ++i) { - if (buffer[i] == UNICODE_NOT_A_CHAR) { - buffer[i] = UNICODE_ZWSP; // zero-width-space for skia - } - } - text = buffer + start; - widths = paint->getTextWidths(text, count << 1, scalarArray); - } else { - text = chars + start; - widths = paint->getTextWidths(text, count << 1, scalarArray); - } - - jfloat totalAdvance = 0; - if (widths < count) { -#if DEBUG_ADVANCES - ALOGD("ICU -- count=%d", widths); -#endif - // Skia operates on code points, not code units, so surrogate pairs return only - // one value. Expand the result so we have one value per UTF-16 code unit. - - // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, - // leaving the remaining widths zero. Not nice. - for (size_t i = 0, p = 0; i < widths; ++i) { - totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); - if (p < count && - text[p] >= UNICODE_FIRST_LOW_SURROGATE && - text[p] < UNICODE_FIRST_PRIVATE_USE && - text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && - text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { - outAdvances[p++] = 0; - } -#if DEBUG_ADVANCES - ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); -#endif - } - } else { -#if DEBUG_ADVANCES - ALOGD("ICU -- count=%d", count); -#endif - for (size_t i = 0; i < count; i++) { - totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); -#if DEBUG_ADVANCES - ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); -#endif - } - } - *outTotalAdvance = totalAdvance; -} - } diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h index a0f9402..fa388c8 100644 --- a/core/jni/android/graphics/TextLayout.h +++ b/core/jni/android/graphics/TextLayout.h @@ -42,17 +42,6 @@ namespace android { #define USE_TEXT_LAYOUT_CACHE 1 enum { - kBidi_LTR = 0, - kBidi_RTL = 1, - kBidi_Default_LTR = 2, - kBidi_Default_RTL = 3, - kBidi_Force_LTR = 4, - kBidi_Force_RTL = 5, - - kBidi_Mask = 0x7 -}; - -enum { kDirection_LTR = 0, kDirection_RTL = 1, @@ -63,28 +52,18 @@ class TextLayout { public: static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, - jint count, jint contextCount, jint dirFlags, + jint count, jint contextCount, jfloat* resultAdvances, jfloat* resultTotalAdvance); - static void getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, - jint count, jint contextCount, jint dirFlags, - jfloat* resultAdvances, jfloat& resultTotalAdvance); - static void getTextPath(SkPaint* paint, const jchar* text, jsize len, - jint bidiFlags, jfloat x, jfloat y, SkPath* path); + jfloat x, jfloat y, SkPath* path); static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len, - int bidiFlags, jfloat hOffset, jfloat vOffset, + jfloat hOffset, jfloat vOffset, SkPath* path, SkCanvas* canvas); private: - static bool needsLayout(const jchar* text, jint len, jint bidiFlags); - static void handleText(SkPaint* paint, const jchar* text, jsize len, - int bidiFlags, jfloat x, jfloat y, SkPath* path); - - static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags, - jfloat* outAdvances, jfloat* outTotalAdvance); + jfloat x, jfloat y, SkPath* path); }; } // namespace android diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 4669c37..60c6183 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -16,17 +16,16 @@ #define LOG_TAG "TextLayoutCache" +#include <utils/JenkinsHash.h> + #include "TextLayoutCache.h" #include "TextLayout.h" #include "SkFontHost.h" #include "SkTypeface_android.h" +#include "HarfBuzzNGFaceSkia.h" #include <unicode/unistr.h> -#include <unicode/normlzr.h> #include <unicode/uchar.h> - -extern "C" { - #include "harfbuzz-unicode.h" -} +#include <hb-icu.h> namespace android { @@ -38,7 +37,7 @@ ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine); TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) : mShaper(shaper), - mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutValue> >::kUnlimitedCapacity), + mCache(LruCache<TextLayoutCacheKey, sp<TextLayoutValue> >::kUnlimitedCapacity), mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)), mCacheHitCount(0), mNanosecondsSaved(0) { init(); @@ -88,7 +87,7 @@ void TextLayoutCache::purgeCaches() { * Caching */ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint, - const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) { + const jchar* text, jint start, jint count, jint contextCount) { AutoMutex _l(mLock); nsecs_t startTime = 0; if (mDebugEnabled) { @@ -96,7 +95,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint, } // Create the key - TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags); + TextLayoutCacheKey key(paint, text, start, count, contextCount); // Get value from cache if possible sp<TextLayoutValue> value = mCache.get(key); @@ -112,7 +111,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint, // Compute advances and store them mShaper->computeValues(value.get(), paint, reinterpret_cast<const UChar*>(key.getText()), start, count, - size_t(contextCount), int(dirFlags)); + size_t(contextCount)); if (mDebugEnabled) { value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime); @@ -199,11 +198,7 @@ void TextLayoutCache::dumpCacheStats() { float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize)); float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000; - size_t bytes = 0; size_t cacheSize = mCache.size(); - for (size_t i = 0; i < cacheSize; i++) { - bytes += mCache.getKeyAt(i).getSize() + mCache.getValueAt(i)->getSize(); - } ALOGD("------------------------------------------------"); ALOGD("Cache stats"); @@ -212,7 +207,7 @@ void TextLayoutCache::dumpCacheStats() { ALOGD("running : %.0f seconds", timeRunningInSec); ALOGD("entries : %d", cacheSize); ALOGD("max size : %d bytes", mMaxSize); - ALOGD("used : %d bytes according to mSize, %d bytes actual", mSize, bytes); + ALOGD("used : %d bytes according to mSize", mSize); ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent); ALOGD("hits : %d", mCacheHitCount); ALOGD("saved : %0.6f ms", mNanosecondsSaved * 0.000001f); @@ -223,14 +218,13 @@ void TextLayoutCache::dumpCacheStats() { * TextLayoutCacheKey */ TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0), - dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), + typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() { } TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text, - size_t start, size_t count, size_t contextCount, int dirFlags) : - start(start), count(count), contextCount(contextCount), - dirFlags(dirFlags) { + size_t start, size_t count, size_t contextCount) : + start(start), count(count), contextCount(contextCount) { textCopy.setTo(text, contextCount); typeface = paint->getTypeface(); textSize = paint->getTextSize(); @@ -247,7 +241,6 @@ TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) : start(other.start), count(other.count), contextCount(other.contextCount), - dirFlags(other.dirFlags), typeface(other.typeface), textSize(other.textSize), textSkewX(other.textSkewX), @@ -286,9 +279,6 @@ int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutC deltaInt = lhs.hinting - rhs.hinting; if (deltaInt != 0) return (deltaInt); - deltaInt = lhs.dirFlags - rhs.dirFlags; - if (deltaInt) return (deltaInt); - deltaInt = lhs.variant - rhs.variant; if (deltaInt) return (deltaInt); @@ -302,6 +292,23 @@ size_t TextLayoutCacheKey::getSize() const { return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; } +hash_t TextLayoutCacheKey::hash() const { + uint32_t hash = JenkinsHashMix(0, start); + hash = JenkinsHashMix(hash, count); + /* contextCount not needed because it's included in text, below */ + hash = JenkinsHashMix(hash, hash_type(typeface)); + hash = JenkinsHashMix(hash, hash_type(textSize)); + hash = JenkinsHashMix(hash, hash_type(textSkewX)); + hash = JenkinsHashMix(hash, hash_type(textScaleX)); + hash = JenkinsHashMix(hash, flags); + hash = JenkinsHashMix(hash, hinting); + hash = JenkinsHashMix(hash, variant); + // Note: leaving out language is not problematic, as equality comparisons + // are still valid - the only bad thing that could happen is collisions. + hash = JenkinsHashMixShorts(hash, getText(), contextCount); + return JenkinsHashWhiten(hash); +} + /** * TextLayoutCacheValue */ @@ -326,20 +333,10 @@ uint32_t TextLayoutValue::getElapsedTime() { return mElapsedTime; } -TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) { +TextLayoutShaper::TextLayoutShaper() { init(); - mFontRec.klass = &harfbuzzSkiaClass; - mFontRec.userData = 0; - - // Note that the scaling values (x_ and y_ppem, x_ and y_scale) will be set - // below, when the paint transform and em unit of the actual shaping font - // are known. - - memset(&mShaperItem, 0, sizeof(mShaperItem)); - - mShaperItem.font = &mFontRec; - mShaperItem.font->userData = &mShapingPaint; + mBuffer = hb_buffer_create(); } void TextLayoutShaper::init() { @@ -351,14 +348,15 @@ void TextLayoutShaper::unrefTypefaces() { } TextLayoutShaper::~TextLayoutShaper() { + hb_buffer_destroy(mBuffer); + unrefTypefaces(); - deleteShaperItemGlyphArrays(); } void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags) { + size_t start, size_t count, size_t contextCount) { - computeValues(paint, chars, start, count, contextCount, dirFlags, + computeValues(paint, chars, start, count, contextCount, &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos); #if DEBUG_ADVANCES ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count, @@ -367,7 +365,7 @@ void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* pain } void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags, + size_t start, size_t count, size_t contextCount, Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) { *outTotalAdvance = 0; @@ -375,110 +373,94 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars, return; } - UBiDiLevel bidiReq = 0; - bool forceLTR = false; - bool forceRTL = false; - - switch (dirFlags) { - case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level - case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level - case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; - case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; - case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR - case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL - } - + UBiDiLevel bidiReq = UBIDI_DEFAULT_LTR; bool useSingleRun = false; - bool isRTL = forceRTL; - if (forceLTR || forceRTL) { - useSingleRun = true; - } else { - UBiDi* bidi = ubidi_open(); - if (bidi) { - UErrorCode status = U_ZERO_ERROR; + bool isRTL = false; + + UBiDi* bidi = ubidi_open(); + if (bidi) { + UErrorCode status = U_ZERO_ERROR; #if DEBUG_GLYPHS - ALOGD("******** ComputeValues -- start"); - ALOGD(" -- string = '%s'", String8(chars + start, count).string()); - ALOGD(" -- start = %d", start); - ALOGD(" -- count = %d", count); - ALOGD(" -- contextCount = %d", contextCount); - ALOGD(" -- bidiReq = %d", bidiReq); + ALOGD("******** ComputeValues -- start"); + ALOGD(" -- string = '%s'", String8(chars + start, count).string()); + ALOGD(" -- start = %d", start); + ALOGD(" -- count = %d", count); + ALOGD(" -- contextCount = %d", contextCount); + ALOGD(" -- bidiReq = %d", bidiReq); #endif - ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status); - if (U_SUCCESS(status)) { - int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl - ssize_t rc = ubidi_countRuns(bidi, &status); + ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status); + if (U_SUCCESS(status)) { + int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl + ssize_t rc = ubidi_countRuns(bidi, &status); #if DEBUG_GLYPHS - ALOGD(" -- dirFlags = %d", dirFlags); - ALOGD(" -- paraDir = %d", paraDir); - ALOGD(" -- run-count = %d", int(rc)); + ALOGD(" -- paraDir = %d", paraDir); + ALOGD(" -- run-count = %d", int(rc)); #endif - if (U_SUCCESS(status) && rc == 1) { - // Normal case: one run, status is ok - isRTL = (paraDir == 1); - useSingleRun = true; - } else if (!U_SUCCESS(status) || rc < 1) { - ALOGW("Need to force to single run -- string = '%s'," - " status = %d, rc = %d", - String8(chars + start, count).string(), status, int(rc)); - isRTL = (paraDir == 1); - useSingleRun = true; - } else { - int32_t end = start + count; - for (size_t i = 0; i < size_t(rc); ++i) { - int32_t startRun = -1; - int32_t lengthRun = -1; - UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun); - - if (startRun == -1 || lengthRun == -1) { - // Something went wrong when getting the visual run, need to clear - // already computed data before doing a single run pass - ALOGW("Visual run is not valid"); - outGlyphs->clear(); - outAdvances->clear(); - outPos->clear(); - *outTotalAdvance = 0; - isRTL = (paraDir == 1); - useSingleRun = true; - break; - } - - if (startRun >= end) { - continue; - } - int32_t endRun = startRun + lengthRun; - if (endRun <= int32_t(start)) { - continue; - } - if (startRun < int32_t(start)) { - startRun = int32_t(start); - } - if (endRun > end) { - endRun = end; - } - - lengthRun = endRun - startRun; - isRTL = (runDir == UBIDI_RTL); + if (U_SUCCESS(status) && rc == 1) { + // Normal case: one run, status is ok + isRTL = (paraDir == 1); + useSingleRun = true; + } else if (!U_SUCCESS(status) || rc < 1) { + ALOGW("Need to force to single run -- string = '%s'," + " status = %d, rc = %d", + String8(chars + start, count).string(), status, int(rc)); + isRTL = (paraDir == 1); + useSingleRun = true; + } else { + int32_t end = start + count; + for (size_t i = 0; i < size_t(rc); ++i) { + int32_t startRun = -1; + int32_t lengthRun = -1; + UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun); + + if (startRun == -1 || lengthRun == -1) { + // Something went wrong when getting the visual run, need to clear + // already computed data before doing a single run pass + ALOGW("Visual run is not valid"); + outGlyphs->clear(); + outAdvances->clear(); + outPos->clear(); + *outTotalAdvance = 0; + isRTL = (paraDir == 1); + useSingleRun = true; + break; + } + + if (startRun >= end) { + continue; + } + int32_t endRun = startRun + lengthRun; + if (endRun <= int32_t(start)) { + continue; + } + if (startRun < int32_t(start)) { + startRun = int32_t(start); + } + if (endRun > end) { + endRun = end; + } + + lengthRun = endRun - startRun; + isRTL = (runDir == UBIDI_RTL); #if DEBUG_GLYPHS - ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d", - i, startRun, lengthRun, isRTL); + ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d", + i, startRun, lengthRun, isRTL); #endif - computeRunValues(paint, chars + startRun, lengthRun, isRTL, - outAdvances, outTotalAdvance, outGlyphs, outPos); + computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL, + outAdvances, outTotalAdvance, outGlyphs, outPos); - } } - } else { - ALOGW("Cannot set Para"); - useSingleRun = true; - isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL); } - ubidi_close(bidi); } else { - ALOGW("Cannot ubidi_open()"); + ALOGW("Cannot set Para"); useSingleRun = true; isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL); } + ubidi_close(bidi); + } else { + ALOGW("Cannot ubidi_open()"); + useSingleRun = true; + isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL); } // Default single run case @@ -487,7 +469,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars, ALOGD("Using a SINGLE BiDi Run " "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL); #endif - computeRunValues(paint, chars + start, count, isRTL, + computeRunValues(paint, chars, start, count, contextCount, isRTL, outAdvances, outTotalAdvance, outGlyphs, outPos); } @@ -497,18 +479,197 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars, #endif } -static void logGlyphs(HB_ShaperItem shaperItem) { - ALOGD(" -- glyphs count=%d", shaperItem.num_glyphs); - for (size_t i = 0; i < shaperItem.num_glyphs; i++) { - ALOGD(" -- glyph[%d] = %d, offset.x = %0.2f, offset.y = %0.2f", i, - shaperItem.glyphs[i], - HBFixedToFloat(shaperItem.offsets[i].x), - HBFixedToFloat(shaperItem.offsets[i].y)); +#define HB_IsHighSurrogate(ucs) \ + (((ucs) & 0xfc00) == 0xd800) + +#define HB_IsLowSurrogate(ucs) \ + (((ucs) & 0xfc00) == 0xdc00) + +#ifndef HB_SurrogateToUcs4 +#define HB_SurrogateToUcs4_(high, low) \ + (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00; +#endif + +#define HB_InvalidCodePoint ~0u + +hb_codepoint_t +utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) { + const uint16_t v = chars[(*iter)++]; + if (HB_IsHighSurrogate(v)) { + // surrogate pair + if (size_t(*iter) >= len) { + // the surrogate is incomplete. + return HB_InvalidCodePoint; + } + const uint16_t v2 = chars[(*iter)++]; + if (!HB_IsLowSurrogate(v2)) { + // invalidate surrogate pair. + (*iter)--; + return HB_InvalidCodePoint; + } + + return HB_SurrogateToUcs4(v, v2); + } + + if (HB_IsLowSurrogate(v)) { + // this isn't a valid code point + return HB_InvalidCodePoint; + } + + return v; +} + +hb_codepoint_t +utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) { + const uint16_t v = chars[(*iter)--]; + if (HB_IsLowSurrogate(v)) { + // surrogate pair + if (*iter < 0) { + // the surrogate is incomplete. + return HB_InvalidCodePoint; + } + const uint16_t v2 = chars[(*iter)--]; + if (!HB_IsHighSurrogate(v2)) { + // invalidate surrogate pair. + (*iter)++; + return HB_InvalidCodePoint; + } + + return HB_SurrogateToUcs4(v2, v); + } + + if (HB_IsHighSurrogate(v)) { + // this isn't a valid code point + return HB_InvalidCodePoint; + } + + return v; +} + +struct ScriptRun { + hb_script_t script; + size_t pos; + size_t length; +}; + +hb_script_t code_point_to_script(hb_codepoint_t codepoint) { + static hb_unicode_funcs_t* u; + if (!u) { + u = hb_icu_get_unicode_funcs(); + } + return hb_unicode_script(u, codepoint); +} + +bool +hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) { + if (size_t(*iter) == len) + return false; + + run->pos = *iter; + const uint32_t init_cp = utf16_to_code_point(chars, len, iter); + const hb_script_t init_script = code_point_to_script(init_cp); + hb_script_t current_script = init_script; + run->script = init_script; + + for (;;) { + if (size_t(*iter) == len) + break; + const ssize_t prev_iter = *iter; + const uint32_t cp = utf16_to_code_point(chars, len, iter); + const hb_script_t script = code_point_to_script(cp); + + if (script != current_script) { + /* BEGIN android-changed + The condition was not correct by doing "a == b == constant" + END android-changed */ + if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) { + // If we started off as inherited, we take whatever we can find. + run->script = script; + current_script = script; + continue; + } else if (script == HB_SCRIPT_INHERITED) { + continue; + } else { + *iter = prev_iter; + break; + } } + } + + if (run->script == HB_SCRIPT_INHERITED) + run->script = HB_SCRIPT_COMMON; + + run->length = *iter - run->pos; + return true; +} + +bool +hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) { + if (*iter == -1) + return false; + + const size_t ending_index = *iter; + const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter); + const hb_script_t init_script = code_point_to_script(init_cp); + hb_script_t current_script = init_script; + run->script = init_script; + + for (;;) { + if (*iter < 0) + break; + const ssize_t prev_iter = *iter; + const uint32_t cp = utf16_to_code_point_prev(chars, len, iter); + const hb_script_t script = code_point_to_script(cp); + + if (script != current_script) { + if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) { + // If we started off as inherited, we take whatever we can find. + run->script = script; + current_script = script; + continue; + } else if (script == HB_SCRIPT_INHERITED) { + /* BEGIN android-changed + We apply the same fix for Chrome to Android. + Chrome team will talk with upsteam about it. + Just assume that whatever follows this combining character is within + the same script. This is incorrect if you had language1 + combining + char + language 2, but that is rare and this code is suspicious + anyway. + END android-changed */ + continue; + } else { + *iter = prev_iter; + break; + } + } + } + + if (run->script == HB_SCRIPT_INHERITED) + run->script = HB_SCRIPT_COMMON; + + run->pos = *iter + 1; + run->length = ending_index - *iter; + return true; } -void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars, - size_t count, bool isRTL, + +static void logGlyphs(hb_buffer_t* buffer) { + unsigned int numGlyphs; + hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs); + hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL); + ALOGD(" -- glyphs count=%d", numGlyphs); + for (size_t i = 0; i < numGlyphs; i++) { + ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i, + info[i].codepoint, + info[i].cluster, + HBFixedToFloat(positions[i].x_advance), + HBFixedToFloat(positions[i].x_offset), + HBFixedToFloat(positions[i].y_offset)); + } +} + +void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars, + size_t start, size_t count, size_t contextCount, bool isRTL, Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) { if (!count) { @@ -520,95 +681,9 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars for (size_t i = 0; i < count; i++) { outAdvances->add(0); } - UErrorCode error = U_ZERO_ERROR; - bool useNormalizedString = false; - for (ssize_t i = count - 1; i >= 0; --i) { - UChar ch1 = chars[i]; - if (::ublock_getCode(ch1) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { - // So we have found a diacritic, let's get now the main code point which is paired - // with it. As we can have several diacritics in a row, we need to iterate back again -#if DEBUG_GLYPHS - ALOGD("The BiDi run '%s' is containing a Diacritic at position %d", - String8(chars, count).string(), int(i)); -#endif - ssize_t j = i - 1; - for (; j >= 0; --j) { - UChar ch2 = chars[j]; - if (::ublock_getCode(ch2) != UBLOCK_COMBINING_DIACRITICAL_MARKS) { - break; - } - } - - // We could not found the main code point, so we will just use the initial chars - if (j < 0) { - break; - } - -#if DEBUG_GLYPHS - ALOGD("Found main code point at index %d", int(j)); -#endif - // We found the main code point, so we can normalize the "chunk" and fill - // the remaining with ZWSP so that the Paint.getTextWidth() APIs will still be able - // to get one advance per char - mBuffer.remove(); - Normalizer::normalize(UnicodeString(chars + j, i - j + 1), - UNORM_NFC, 0 /* no options */, mBuffer, error); - if (U_SUCCESS(error)) { - if (!useNormalizedString) { - useNormalizedString = true; - mNormalizedString.setTo(false /* not terminated*/, chars, count); - } - // Set the normalized chars - for (ssize_t k = j; k < j + mBuffer.length(); ++k) { - mNormalizedString.setCharAt(k, mBuffer.charAt(k - j)); - } - // Fill the remain part with ZWSP (ZWNJ and ZWJ would lead to weird results - // because some fonts are missing those glyphs) - for (ssize_t k = j + mBuffer.length(); k <= i; ++k) { - mNormalizedString.setCharAt(k, UNICODE_ZWSP); - } - } - i = j - 1; - } - } - - // Reverse "BiDi mirrored chars" in RTL mode only - // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt - // This is a workaround because Harfbuzz is not able to do mirroring in all cases and - // script-run splitting with Harfbuzz is splitting on parenthesis - if (isRTL) { - for (ssize_t i = 0; i < ssize_t(count); i++) { - UChar32 ch = chars[i]; - if (!u_isMirrored(ch)) continue; - if (!useNormalizedString) { - useNormalizedString = true; - mNormalizedString.setTo(false /* not terminated*/, chars, count); - } - UChar result = (UChar) u_charMirror(ch); - mNormalizedString.setCharAt(i, result); -#if DEBUG_GLYPHS - ALOGD("Rewriting codepoint '%d' to '%d' at position %d", - ch, mNormalizedString[i], int(i)); -#endif - } - } - -#if DEBUG_GLYPHS - if (useNormalizedString) { - ALOGD("Will use normalized string '%s', length = %d", - String8(mNormalizedString.getTerminatedBuffer(), - mNormalizedString.length()).string(), - mNormalizedString.length()); - } else { - ALOGD("Normalization is not needed or cannot be done, using initial string"); - } -#endif - - assert(mNormalizedString.length() == count); // Set the string properties - mShaperItem.string = useNormalizedString ? mNormalizedString.getTerminatedBuffer() : chars; - mShaperItem.stringLength = count; + const UChar* chars = contextChars + start; // Define shaping paint properties mShapingPaint.setTextSize(paint->getTextSize()); @@ -622,130 +697,66 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script // into the shaperItem - ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0; - unsigned numCodePoints = 0; + ssize_t indexFontRun = isRTL ? count - 1 : 0; jfloat totalAdvance = *outTotalAdvance; + ScriptRun run; // relative to chars while ((isRTL) ? - hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string, - mShaperItem.stringLength, &indexFontRun): - hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, mShaperItem.string, - mShaperItem.stringLength, &indexFontRun)) { - - ssize_t startScriptRun = mShaperItem.item.pos; - size_t countScriptRun = mShaperItem.item.length; - ssize_t endScriptRun = startScriptRun + countScriptRun; + hb_utf16_script_run_prev(&run, chars, count, &indexFontRun): + hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) { #if DEBUG_GLYPHS ALOGD("-------- Start of Script Run --------"); ALOGD("Shaping Script Run with"); ALOGD(" -- isRTL = %d", isRTL); - ALOGD(" -- HB script = %d", mShaperItem.item.script); - ALOGD(" -- startFontRun = %d", int(startScriptRun)); - ALOGD(" -- endFontRun = %d", int(endScriptRun)); - ALOGD(" -- countFontRun = %d", countScriptRun); - ALOGD(" -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string()); + ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script)); + ALOGD(" -- run.pos = %d", int(run.pos)); + ALOGD(" -- run.length = %d", int(run.length)); + ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string()); ALOGD(" -- string = '%s'", String8(chars, count).string()); #endif + hb_buffer_reset(mBuffer); + // Note: if we want to set unicode functions, etc., this is the place. + + hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); + hb_buffer_set_script(mBuffer, run.script); + // Should set language here (for bug 7004056) + hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length); + // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs // and shape the Font run - size_t glyphBaseCount = shapeFontRun(paint, isRTL); + size_t glyphBaseCount = shapeFontRun(paint); + unsigned int numGlyphs; + hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs); + hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL); #if DEBUG_GLYPHS ALOGD("Got from Harfbuzz"); ALOGD(" -- glyphBaseCount = %d", glyphBaseCount); - ALOGD(" -- num_glypth = %d", mShaperItem.num_glyphs); - ALOGD(" -- kerning_applied = %d", mShaperItem.kerning_applied); + ALOGD(" -- num_glyph = %d", numGlyphs); ALOGD(" -- isDevKernText = %d", paint->isDevKernText()); + ALOGD(" -- initial totalAdvance = %f", totalAdvance); - logGlyphs(mShaperItem); + logGlyphs(mBuffer); #endif - if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) { -#if DEBUG_GLYPHS - ALOGD("Advances array is empty or num_glypth = 0"); -#endif - continue; + for (size_t i = 0; i < numGlyphs; i++) { + size_t cluster = info[i].cluster - start; + float xAdvance = HBFixedToFloat(positions[i].x_advance); + outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster); + outGlyphs->add(info[i].codepoint + glyphBaseCount); + float xo = HBFixedToFloat(positions[i].x_offset); + float yo = -HBFixedToFloat(positions[i].y_offset); + outPos->add(totalAdvance + xo + yo * skewX); + outPos->add(yo); + totalAdvance += xAdvance; } - -#if DEBUG_GLYPHS - ALOGD("Returned logclusters"); - for (size_t i = 0; i < mShaperItem.num_glyphs; i++) { - ALOGD(" -- lc[%d] = %d, hb-adv[%d] = %0.2f", i, mShaperItem.log_clusters[i], - i, HBFixedToFloat(mShaperItem.advances[i])); - } -#endif - jfloat totalFontRunAdvance = 0; - size_t clusterStart = 0; - for (size_t i = 0; i < countScriptRun; i++) { - size_t cluster = mShaperItem.log_clusters[i]; - size_t clusterNext = i == countScriptRun - 1 ? mShaperItem.num_glyphs : - mShaperItem.log_clusters[i + 1]; - if (cluster != clusterNext) { - jfloat advance = 0; - // The advance for the cluster is the sum of the advances of all glyphs within - // the cluster. - for (size_t j = cluster; j < clusterNext; j++) { - advance += HBFixedToFloat(mShaperItem.advances[j]); - } - totalFontRunAdvance += advance; - outAdvances->replaceAt(advance, startScriptRun + clusterStart); - clusterStart = i + 1; - } - } - -#if DEBUG_ADVANCES - ALOGD("Returned advances"); - for (size_t i = 0; i < countScriptRun; i++) { - ALOGD(" -- hb-adv[%d] = %0.2f, log_clusters = %d, total = %0.2f", i, - (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance); - } -#endif - - // Get Glyphs and reverse them in place if RTL - if (outGlyphs) { - size_t countGlyphs = mShaperItem.num_glyphs; -#if DEBUG_GLYPHS - ALOGD("Returned script run glyphs -- count = %d", countGlyphs); -#endif - for (size_t i = 0; i < countGlyphs; i++) { - jchar glyph = glyphBaseCount + - (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i]; -#if DEBUG_GLYPHS - ALOGD(" -- glyph[%d] = %d", i, glyph); -#endif - outGlyphs->add(glyph); - } - } - - // Get glyph positions (and reverse them in place if RTL) - if (outPos) { - size_t countGlyphs = mShaperItem.num_glyphs; - jfloat x = totalAdvance; - for (size_t i = 0; i < countGlyphs; i++) { - size_t index = (!isRTL) ? i : countGlyphs - 1 - i; - float xo = HBFixedToFloat(mShaperItem.offsets[index].x); - float yo = HBFixedToFloat(mShaperItem.offsets[index].y); - // Apply skewX component of transform to position offsets. Note - // that scale has already been applied through x_ and y_scale - // set in the mFontRec. - outPos->add(x + xo + yo * skewX); - outPos->add(yo); -#if DEBUG_GLYPHS - ALOGD(" -- hb adv[%d] = %f, log_cluster[%d] = %d", - index, HBFixedToFloat(mShaperItem.advances[index]), - index, mShaperItem.log_clusters[index]); -#endif - x += HBFixedToFloat(mShaperItem.advances[index]); - } - } - - totalAdvance += totalFontRunAdvance; } *outTotalAdvance = totalAdvance; #if DEBUG_GLYPHS + ALOGD(" -- final totalAdvance = %f", totalAdvance); ALOGD("-------- End of Script Run --------"); #endif } @@ -753,43 +764,38 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars /** * Return the first typeface in the logical change, starting with this typeface, * that contains the specified unichar, or NULL if none is found. - * - * Note that this function does _not_ increment the reference count on the typeface, as the - * assumption is that its lifetime is managed elsewhere - in particular, the fallback typefaces - * for the default font live in a global cache. */ SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface, - HB_Script script) { + hb_script_t script) { SkTypeface::Style currentStyle = SkTypeface::kNormal; if (typeface) { currentStyle = typeface->style(); } - typeface = SkCreateTypefaceForScript(script, currentStyle); + typeface = SkCreateTypefaceForScriptNG(script, currentStyle); #if DEBUG_GLYPHS - ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle); + ALOGD("Using Harfbuzz Script %c%c%c%c, Style %d", HB_UNTAG(script), currentStyle); #endif return typeface; } -bool TextLayoutShaper::isComplexScript(HB_Script script) { +bool TextLayoutShaper::isComplexScript(hb_script_t script) { switch (script) { - case HB_Script_Common: - case HB_Script_Greek: - case HB_Script_Cyrillic: - case HB_Script_Hangul: - case HB_Script_Inherited: + case HB_SCRIPT_COMMON: + case HB_SCRIPT_GREEK: + case HB_SCRIPT_CYRILLIC: + case HB_SCRIPT_HANGUL: + case HB_SCRIPT_INHERITED: + case HB_SCRIPT_HAN: + case HB_SCRIPT_KATAKANA: + case HB_SCRIPT_HIRAGANA: return false; default: return true; } } -size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) { - // Reset kerning - mShaperItem.kerning_applied = false; - +size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) { // Update Harfbuzz Shaper - mShaperItem.item.bidiLevel = isRTL; SkTypeface* typeface = paint->getTypeface(); @@ -798,20 +804,23 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) { // when we are shaping any script that needs to use a fallback Font. // If we are a "common" script we dont need to shift size_t baseGlyphCount = 0; - SkUnichar firstUnichar = 0; - if (isComplexScript(mShaperItem.item.script)) { - const uint16_t* text16 = (const uint16_t*) (mShaperItem.string + mShaperItem.item.pos); - const uint16_t* text16End = text16 + mShaperItem.item.length; - firstUnichar = SkUTF16_NextUnichar(&text16); - while (firstUnichar == ' ' && text16 < text16End) { - firstUnichar = SkUTF16_NextUnichar(&text16); + hb_codepoint_t firstUnichar = 0; + if (isComplexScript(hb_buffer_get_script(mBuffer))) { + unsigned int numGlyphs; + hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs); + for (size_t i = 0; i < numGlyphs; i++) { + firstUnichar = info[i].codepoint; + if (firstUnichar != ' ') { + break; + } } baseGlyphCount = paint->getBaseGlyphCount(firstUnichar); } if (baseGlyphCount != 0) { - typeface = typefaceForScript(paint, typeface, mShaperItem.item.script); + typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer)); if (!typeface) { + baseGlyphCount = 0; typeface = mDefaultTypeface; SkSafeRef(typeface); #if DEBUG_GLYPHS @@ -829,94 +838,44 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) { } mShapingPaint.setTypeface(typeface); - mShaperItem.face = getCachedHBFace(typeface); - - int textSize = paint->getTextSize(); - float scaleX = paint->getTextScaleX(); - mFontRec.x_ppem = floor(scaleX * textSize + 0.5); - mFontRec.y_ppem = textSize; - uint32_t unitsPerEm = SkFontHost::GetUnitsPerEm(typeface->uniqueID()); - // x_ and y_scale are the conversion factors from font design space - // (unitsPerEm) to 1/64th of device pixels in 16.16 format. - const int kDevicePixelFraction = 64; - const int kMultiplyFor16Dot16 = 1 << 16; - float emScale = kDevicePixelFraction * kMultiplyFor16Dot16 / (float)unitsPerEm; - mFontRec.x_scale = emScale * scaleX * textSize; - mFontRec.y_scale = emScale * textSize; - -#if DEBUG_GLYPHS - ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p", - typeface, typeface->uniqueID(), mShaperItem.face); -#endif - SkSafeUnref(typeface); - - // Shape - assert(mShaperItem.item.length > 0); // Harfbuzz will overwrite other memory if length is 0. - size_t size = mShaperItem.item.length * 3 / 2; - while (!doShaping(size)) { - // We overflowed our glyph arrays. Resize and retry. - // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. - size = mShaperItem.num_glyphs * 2; - } - return baseGlyphCount; -} + hb_face_t* face = referenceCachedHBFace(typeface); -bool TextLayoutShaper::doShaping(size_t size) { - if (size > mShaperItemGlyphArraySize) { - deleteShaperItemGlyphArrays(); - createShaperItemGlyphArrays(size); - } - mShaperItem.num_glyphs = mShaperItemGlyphArraySize; - memset(mShaperItem.offsets, 0, mShaperItem.num_glyphs * sizeof(HB_FixedPoint)); - return HB_ShapeItem(&mShaperItem); -} + float sizeY = paint->getTextSize(); + float sizeX = sizeY * paint->getTextScaleX(); + hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY); + hb_face_destroy(face); -void TextLayoutShaper::createShaperItemGlyphArrays(size_t size) { #if DEBUG_GLYPHS - ALOGD("Creating Glyph Arrays with size = %d", size); + ALOGD("Run typeface = %p, uniqueID = %d, face = %p", + typeface, typeface->uniqueID(), face); #endif - mShaperItemGlyphArraySize = size; - - // These arrays are all indexed by glyph. - mShaperItem.glyphs = new HB_Glyph[size]; - mShaperItem.attributes = new HB_GlyphAttributes[size]; - mShaperItem.advances = new HB_Fixed[size]; - mShaperItem.offsets = new HB_FixedPoint[size]; + SkSafeUnref(typeface); - // Although the log_clusters array is indexed by character, Harfbuzz expects that - // it is big enough to hold one element per glyph. So we allocate log_clusters along - // with the other glyph arrays above. - mShaperItem.log_clusters = new unsigned short[size]; -} + hb_shape(font, mBuffer, NULL, 0); + hb_font_destroy(font); -void TextLayoutShaper::deleteShaperItemGlyphArrays() { - delete[] mShaperItem.glyphs; - delete[] mShaperItem.attributes; - delete[] mShaperItem.advances; - delete[] mShaperItem.offsets; - delete[] mShaperItem.log_clusters; + return baseGlyphCount; } -HB_Face TextLayoutShaper::getCachedHBFace(SkTypeface* typeface) { +hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) { SkFontID fontId = typeface->uniqueID(); ssize_t index = mCachedHBFaces.indexOfKey(fontId); if (index >= 0) { - return mCachedHBFaces.valueAt(index); + return hb_face_reference(mCachedHBFaces.valueAt(index)); } - HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable); - if (face) { + // TODO: destroy function + hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL); #if DEBUG_GLYPHS - ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface); + ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface); #endif - mCachedHBFaces.add(fontId, face); - } - return face; + mCachedHBFaces.add(fontId, face); + return hb_face_reference(face); } void TextLayoutShaper::purgeCaches() { size_t cacheSize = mCachedHBFaces.size(); for (size_t i = 0; i < cacheSize; i++) { - HB_FreeFace(mCachedHBFaces.valueAt(i)); + hb_face_destroy(mCachedHBFaces.valueAt(i)); } mCachedHBFaces.clear(); unrefTypefaces(); @@ -938,11 +897,11 @@ TextLayoutEngine::~TextLayoutEngine() { } sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text, - jint start, jint count, jint contextCount, jint dirFlags) { + jint start, jint count, jint contextCount) { sp<TextLayoutValue> value; #if USE_TEXT_LAYOUT_CACHE value = mTextLayoutCache->getValue(paint, text, start, count, - contextCount, dirFlags); + contextCount); if (value == NULL) { ALOGE("Cannot get TextLayoutCache value for text = '%s'", String8(text + start, count).string()); @@ -950,7 +909,7 @@ sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar #else value = new TextLayoutValue(count); mShaper->computeValues(value.get(), paint, - reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags); + reinterpret_cast<const UChar*>(text), start, count, contextCount); #endif return value; } diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h index 9994393..6858c0e 100644 --- a/core/jni/android/graphics/TextLayoutCache.h +++ b/core/jni/android/graphics/TextLayoutCache.h @@ -22,24 +22,22 @@ #include <stddef.h> #include <utils/threads.h> #include <utils/String16.h> -#include <utils/GenerationCache.h> +#include <utils/LruCache.h> #include <utils/KeyedVector.h> -#include <utils/Compare.h> #include <utils/RefBase.h> #include <utils/Singleton.h> +#include <SkAutoKern.h> +#include <SkLanguage.h> #include <SkPaint.h> #include <SkTemplates.h> +#include <SkTypeface.h> #include <SkUtils.h> -#include <SkAutoKern.h> -#include <SkLanguage.h> #include <unicode/ubidi.h> -#include <unicode/ushape.h> #include <unicode/unistr.h> -#include "HarfbuzzSkia.h" -#include "harfbuzz-shaper.h" +#include <hb.h> #include <android_runtime/AndroidRuntime.h> @@ -72,7 +70,7 @@ public: TextLayoutCacheKey(); TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count, - size_t contextCount, int dirFlags); + size_t contextCount); TextLayoutCacheKey(const TextLayoutCacheKey& other); @@ -85,12 +83,20 @@ public: inline const UChar* getText() const { return textCopy.string(); } + bool operator==(const TextLayoutCacheKey& other) const { + return compare(*this, other) == 0; + } + + bool operator!=(const TextLayoutCacheKey& other) const { + return compare(*this, other) != 0; + } + + hash_t hash() const; private: String16 textCopy; size_t start; size_t count; size_t contextCount; - int dirFlags; SkTypeface* typeface; SkScalar textSize; SkScalar textSkewX; @@ -110,6 +116,10 @@ inline int compare_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& return TextLayoutCacheKey::compare(lhs, rhs); } +inline hash_t hash_type(const TextLayoutCacheKey& key) { + return key.hash(); +} + /* * TextLayoutValue is the Cache value */ @@ -170,20 +180,15 @@ public: virtual ~TextLayoutShaper(); void computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags); + size_t start, size_t count, size_t contextCount); void purgeCaches(); private: /** - * Harfbuzz shaper item - */ - HB_ShaperItem mShaperItem; - - /** - * Harfbuzz font + * Harfbuzz buffer for shaping */ - HB_FontRec mFontRec; + hb_buffer_t* mBuffer; /** * Skia Paint used for shaping @@ -198,48 +203,30 @@ private: /** * Cache of Harfbuzz faces */ - KeyedVector<SkFontID, HB_Face> mCachedHBFaces; - - /** - * Cache of glyph array size - */ - size_t mShaperItemGlyphArraySize; - - /** - * Buffer for containing the ICU normalized form of a run - */ - UnicodeString mNormalizedString; - - /** - * Buffer for normalizing a piece of a run with ICU - */ - UnicodeString mBuffer; + KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces; void init(); void unrefTypefaces(); SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface, - HB_Script script); + hb_script_t script); - size_t shapeFontRun(const SkPaint* paint, bool isRTL); + size_t shapeFontRun(const SkPaint* paint); void computeValues(const SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags, + size_t start, size_t count, size_t contextCount, Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos); void computeRunValues(const SkPaint* paint, const UChar* chars, - size_t count, bool isRTL, + size_t start, size_t count, size_t contextCount, bool isRTL, Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos); - SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style); - HB_Face getCachedHBFace(SkTypeface* typeface); + SkTypeface* setCachedTypeface(SkTypeface** typeface, hb_script_t script, SkTypeface::Style style); + hb_face_t* referenceCachedHBFace(SkTypeface* typeface); - bool doShaping(size_t size); - void createShaperItemGlyphArrays(size_t size); - void deleteShaperItemGlyphArrays(); - bool isComplexScript(HB_Script script); + bool isComplexScript(hb_script_t script); }; // TextLayoutShaper @@ -264,7 +251,7 @@ public: void operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc); sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start, - jint count, jint contextCount, jint dirFlags); + jint count, jint contextCount); /** * Clear the cache @@ -276,7 +263,7 @@ private: Mutex mLock; bool mInitialized; - GenerationCache<TextLayoutCacheKey, sp<TextLayoutValue> > mCache; + LruCache<TextLayoutCacheKey, sp<TextLayoutValue> > mCache; uint32_t mSize; uint32_t mMaxSize; @@ -316,7 +303,7 @@ public: * the call. Be careful of this when doing optimization. **/ sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start, - jint count, jint contextCount, jint dirFlags); + jint count, jint contextCount); void purgeCaches(); diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp index 634efa6..44af199 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -557,9 +557,9 @@ void nativeUtilsClassInit(JNIEnv *env, jclass clazz) } extern void setGLDebugLevel(int level); -void nativeEnableTracing(JNIEnv *env, jclass clazz) +void setTracingLevel(JNIEnv *env, jclass clazz, jint level) { - setGLDebugLevel(1); + setGLDebugLevel(level); } static int checkFormat(SkBitmap::Config config, int format, int type) @@ -1032,7 +1032,7 @@ static JNINativeMethod gUtilsMethods[] = { { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, - { "native_enableTracing", "()V", (void*)nativeEnableTracing }, + { "setTracingLevel", "(I)V", (void*)setTracingLevel }, }; static JNINativeMethod gEtc1Methods[] = { diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 67d831c..7c65662 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -22,11 +22,13 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include <android_runtime/android_graphics_SurfaceTexture.h> +#include <android_runtime/android_view_Surface.h> #include <cutils/properties.h> #include <utils/Vector.h> -#include <gui/SurfaceTexture.h> +#include <gui/GLConsumer.h> #include <gui/Surface.h> #include <camera/Camera.h> #include <binder/IMemory.h> @@ -35,8 +37,6 @@ using namespace android; struct fields_t { jfieldID context; - jfieldID surface; - jfieldID surfaceTexture; jfieldID facing; jfieldID orientation; jfieldID canDisableShutterSound; @@ -537,10 +537,8 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, sp<Camera> camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; - sp<Surface> surface = NULL; - if (jSurface != NULL) { - surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); - } + sp<Surface> surface = android_view_Surface_getSurface(env, jSurface); + if (camera->setPreviewDisplay(surface) != NO_ERROR) { jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); } @@ -555,8 +553,8 @@ static void android_hardware_Camera_setPreviewTexture(JNIEnv *env, sp<BufferQueue> bufferQueue = NULL; if (jSurfaceTexture != NULL) { - sp<SurfaceTexture> surfaceTexture = reinterpret_cast<SurfaceTexture*>(env->GetIntField( - jSurfaceTexture, fields.surfaceTexture)); + sp<GLConsumer> surfaceTexture = + SurfaceTexture_getSurfaceTexture(env, jSurfaceTexture); if (surfaceTexture != NULL) { bufferQueue = surfaceTexture->getBufferQueue(); } @@ -965,9 +963,6 @@ int register_android_hardware_Camera(JNIEnv *env) { field fields_to_find[] = { { "android/hardware/Camera", "mNativeContext", "I", &fields.context }, - { "android/view/Surface", ANDROID_VIEW_SURFACE_JNI_ID, "I", &fields.surface }, - { "android/graphics/SurfaceTexture", - ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "I", &fields.surfaceTexture }, { "android/hardware/Camera$CameraInfo", "facing", "I", &fields.facing }, { "android/hardware/Camera$CameraInfo", "orientation", "I", &fields.orientation }, { "android/hardware/Camera$CameraInfo", "canDisableShutterSound", "Z", diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 3c1b9c8..e8a6569 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -16,7 +16,8 @@ #define LOG_TAG "SensorManager" -#include "utils/Log.h" +#include <utils/Log.h> +#include <utils/Looper.h> #include <gui/Sensor.h> #include <gui/SensorManager.h> @@ -24,7 +25,13 @@ #include "jni.h" #include "JNIHelp.h" +#include "android_os_MessageQueue.h" +#include <android_runtime/AndroidRuntime.h> +static struct { + jclass clazz; + jmethodID dispatchSensorEvent; +} gSensorEventQueueClassInfo; namespace android { @@ -41,20 +48,29 @@ struct SensorOffsets jfieldID minDelay; } gSensorOffsets; + /* * The method below are not thread-safe and not intended to be */ - -static jint -sensors_module_init(JNIEnv *env, jclass clazz) +static void +nativeClassInit (JNIEnv *_env, jclass _this) { - SensorManager::getInstance(); - return 0; + jclass sensorClass = _env->FindClass("android/hardware/Sensor"); + SensorOffsets& sensorOffsets = gSensorOffsets; + sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;"); + sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;"); + sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I"); + sensorOffsets.handle = _env->GetFieldID(sensorClass, "mHandle", "I"); + sensorOffsets.type = _env->GetFieldID(sensorClass, "mType", "I"); + sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F"); + sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F"); + sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F"); + sensorOffsets.minDelay = _env->GetFieldID(sensorClass, "mMinDelay", "I"); } static jint -sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint next) +nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) { SensorManager& mgr(SensorManager::getInstance()); @@ -82,106 +98,161 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint } //---------------------------------------------------------------------------- -static jint -sensors_create_queue(JNIEnv *env, jclass clazz) -{ - SensorManager& mgr(SensorManager::getInstance()); - sp<SensorEventQueue> queue(mgr.createEventQueue()); - queue->incStrong(clazz); - return reinterpret_cast<int>(queue.get()); -} -static void -sensors_destroy_queue(JNIEnv *env, jclass clazz, jint nativeQueue) -{ - sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue)); - if (queue != 0) { - queue->decStrong(clazz); +class Receiver : public LooperCallback { + sp<SensorEventQueue> mSensorQueue; + sp<MessageQueue> mMessageQueue; + jobject mReceiverObject; + jfloatArray mScratch; +public: + Receiver(const sp<SensorEventQueue>& sensorQueue, + const sp<MessageQueue>& messageQueue, + jobject receiverObject, jfloatArray scratch) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mSensorQueue = sensorQueue; + mMessageQueue = messageQueue; + mReceiverObject = env->NewGlobalRef(receiverObject); + mScratch = (jfloatArray)env->NewGlobalRef(scratch); + } + ~Receiver() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mReceiverObject); + env->DeleteGlobalRef(mScratch); + } + sp<SensorEventQueue> getSensorEventQueue() const { + return mSensorQueue; } -} -static jboolean -sensors_enable_sensor(JNIEnv *env, jclass clazz, - jint nativeQueue, jstring name, jint sensor, jint delay) -{ - sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue)); - if (queue == 0) return JNI_FALSE; - status_t res; - if (delay >= 0) { - res = queue->enableSensor(sensor, delay); - } else { - res = queue->disableSensor(sensor); + void destroy() { + mMessageQueue->getLooper()->removeFd( mSensorQueue->getFd() ); } - return res == NO_ERROR ? true : false; -} -static jint -sensors_data_poll(JNIEnv *env, jclass clazz, jint nativeQueue, - jfloatArray values, jintArray status, jlongArray timestamp) -{ - sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue)); - if (queue == 0) return -1; - - status_t res; - ASensorEvent event; - - res = queue->read(&event, 1); - if (res == 0) { - res = queue->waitForEvent(); - if (res != NO_ERROR) - return -1; - // here we're guaranteed to have an event - res = queue->read(&event, 1); - ALOGE_IF(res==0, "sensors_data_poll: nothing to read after waitForEvent()"); +private: + virtual void onFirstRef() { + LooperCallback::onFirstRef(); + mMessageQueue->getLooper()->addFd(mSensorQueue->getFd(), 0, + ALOOPER_EVENT_INPUT, this, mSensorQueue.get()); } - if (res <= 0) { - return -1; + + virtual int handleEvent(int fd, int events, void* data) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + sp<SensorEventQueue> q = reinterpret_cast<SensorEventQueue *>(data); + ssize_t n; + ASensorEvent buffer[16]; + while ((n = q->read(buffer, 16)) > 0) { + for (int i=0 ; i<n ; i++) { + + env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data); + + env->CallVoidMethod(mReceiverObject, + gSensorEventQueueClassInfo.dispatchSensorEvent, + buffer[i].sensor, + mScratch, + buffer[i].vector.status, + buffer[i].timestamp); + + if (env->ExceptionCheck()) { + ALOGE("Exception dispatching input event."); + return 1; + } + } + } + if (n<0 && n != -EAGAIN) { + // FIXME: error receiving events, what to do in this case? + } + + return 1; } +}; - jint accuracy = event.vector.status; - env->SetFloatArrayRegion(values, 0, 3, event.vector.v); - env->SetIntArrayRegion(status, 0, 1, &accuracy); - env->SetLongArrayRegion(timestamp, 0, 1, &event.timestamp); +static jint nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQ, jobject msgQ, jfloatArray scratch) { + SensorManager& mgr(SensorManager::getInstance()); + sp<SensorEventQueue> queue(mgr.createEventQueue()); + + sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ); + if (messageQueue == NULL) { + jniThrowRuntimeException(env, "MessageQueue is not initialized."); + return 0; + } - return event.sensor; + sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQ, scratch); + receiver->incStrong(clazz); + return jint(receiver.get()); } -static void -nativeClassInit (JNIEnv *_env, jclass _this) -{ - jclass sensorClass = _env->FindClass("android/hardware/Sensor"); - SensorOffsets& sensorOffsets = gSensorOffsets; - sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;"); - sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;"); - sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I"); - sensorOffsets.handle = _env->GetFieldID(sensorClass, "mHandle", "I"); - sensorOffsets.type = _env->GetFieldID(sensorClass, "mType", "I"); - sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F"); - sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F"); - sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F"); - sensorOffsets.minDelay = _env->GetFieldID(sensorClass, "mMinDelay", "I"); +static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle, jint us) { + sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); + return receiver->getSensorEventQueue()->enableSensor(handle, us); } -static JNINativeMethod gMethods[] = { - {"nativeClassInit", "()V", (void*)nativeClassInit }, - {"sensors_module_init","()I", (void*)sensors_module_init }, - {"sensors_module_get_next_sensor","(Landroid/hardware/Sensor;I)I", - (void*)sensors_module_get_next_sensor }, +static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle) { + sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); + return receiver->getSensorEventQueue()->disableSensor(handle); +} + +static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jint eventQ, jint handle) { + sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); + receiver->destroy(); + receiver->decStrong(clazz); +} + + +//---------------------------------------------------------------------------- + +static JNINativeMethod gSystemSensorManagerMethods[] = { + {"nativeClassInit", + "()V", + (void*)nativeClassInit }, + + {"nativeGetNextSensor", + "(Landroid/hardware/Sensor;I)I", + (void*)nativeGetNextSensor }, +}; - {"sensors_create_queue", "()I", (void*)sensors_create_queue }, - {"sensors_destroy_queue", "(I)V", (void*)sensors_destroy_queue }, - {"sensors_enable_sensor", "(ILjava/lang/String;II)Z", - (void*)sensors_enable_sensor }, +static JNINativeMethod gSensorEventQueueMethods[] = { + {"nativeInitSensorEventQueue", + "(Landroid/hardware/SystemSensorManager$SensorEventQueue;Landroid/os/MessageQueue;[F)I", + (void*)nativeInitSensorEventQueue }, - {"sensors_data_poll", "(I[F[I[J)I", (void*)sensors_data_poll }, + {"nativeEnableSensor", + "(III)I", + (void*)nativeEnableSensor }, + + {"nativeDisableSensor", + "(II)I", + (void*)nativeDisableSensor }, + + {"nativeDestroySensorEventQueue", + "(I)V", + (void*)nativeDestroySensorEventQueue }, }; }; // namespace android using namespace android; +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + int register_android_hardware_SensorManager(JNIEnv *env) { - return jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager", - gMethods, NELEM(gMethods)); + jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager", + gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods)); + + jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$SensorEventQueue", + gSensorEventQueueMethods, NELEM(gSensorEventQueueMethods)); + + FIND_CLASS(gSensorEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$SensorEventQueue"); + + GET_METHOD_ID(gSensorEventQueueClassInfo.dispatchSensorEvent, + gSensorEventQueueClassInfo.clazz, + "dispatchSensorEvent", "(I[FIJ)V"); + + return 0; } diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index c76cb64..da2f874 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -224,10 +224,6 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, // create an uninitialized AudioRecord object sp<AudioRecord> lpRecorder = new AudioRecord(); - if (lpRecorder == NULL) { - ALOGE("Error creating AudioRecord instance."); - return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; - } // create the callback information: // this data will be passed with every AudioRecord callback @@ -511,7 +507,7 @@ static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject th ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat); - int frameCount = 0; + size_t frameCount = 0; status_t result = AudioRecord::getMinFrameCount(&frameCount, sampleRateInHertz, (audioFormat == javaAudioRecordFields.PCM16 ? diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 631cdae..0827f7c 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -76,6 +76,15 @@ android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, } static jboolean +android_media_AudioSystem_isStreamActiveRemotely(JNIEnv *env, jobject thiz, jint stream, + jint inPastMs) +{ + bool state = false; + AudioSystem::isStreamActiveRemotely((audio_stream_type_t) stream, &state, inPastMs); + return state; +} + +static jboolean android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) { bool state = false; @@ -270,6 +279,7 @@ static JNINativeMethod gMethods[] = { {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, + {"isStreamActiveRemotely","(II)Z", (void *)android_media_AudioSystem_isStreamActiveRemotely}, {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 7e5263d..9a3736c 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -49,8 +49,6 @@ struct fields_t { jmethodID postNativeEventInJava; //... event post callback method int PCM16; //... format constants int PCM8; //... format constants - int MODE_STREAM; //... memory mode - int MODE_STATIC; //... memory mode jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object jfieldID jniData; // stores in Java additional resources used by the native AudioTrack }; @@ -63,6 +61,10 @@ struct audiotrack_callback_cookie { Condition cond; }; +// keep these values in sync with AudioTrack.java +#define MODE_STATIC 0 +#define MODE_STREAM 1 + // ---------------------------------------------------------------------------- class AudioTrackJniStorage { public: @@ -206,8 +208,8 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th { ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d", sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes); - int afSampleRate; - int afFrameCount; + uint32_t afSampleRate; + size_t afFrameCount; if (AudioSystem::getOutputFrameCount(&afFrameCount, (audio_stream_type_t) streamType) != NO_ERROR) { ALOGE("Error creating AudioTrack: Could not get AudioSystem frame count."); @@ -258,7 +260,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th // so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled // in android_media_AudioTrack_native_write_byte() if ((audioFormat == javaAudioTrackFields.PCM8) - && (memoryMode == javaAudioTrackFields.MODE_STATIC)) { + && (memoryMode == MODE_STATIC)) { ALOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \ buff size of %dbytes, switching to 16bit, buff size of %dbytes", buffSizeInBytes, 2*buffSizeInBytes); @@ -295,10 +297,6 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th // create the native AudioTrack object sp<AudioTrack> lpTrack = new AudioTrack(); - if (lpTrack == NULL) { - ALOGE("Error creating uninitialized AudioTrack"); - return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; - } // initialize the callback information: // this data will be passed with every AudioTrack callback @@ -310,7 +308,8 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th lpJniStorage->mCallbackData.busy = false; // initialize the native AudioTrack object - if (memoryMode == javaAudioTrackFields.MODE_STREAM) { + switch (memoryMode) { + case MODE_STREAM: lpTrack->set( atStreamType,// stream type @@ -324,8 +323,9 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th 0,// shared mem true,// thread can call Java sessionId);// audio session ID + break; - } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) { + case MODE_STATIC: // AudioTrack is using shared memory if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { @@ -345,6 +345,11 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th lpJniStorage->mMemBase,// shared mem true,// thread can call Java sessionId);// audio session ID + break; + + default: + ALOGE("Unknown mode %d", memoryMode); + goto native_init_failure; } if (lpTrack->initCheck() != NO_ERROR) { @@ -750,7 +755,7 @@ static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz, jint javaStreamType) { - int afSamplingRate; + uint32_t afSamplingRate; // convert the stream type from Java to native value // FIXME: code duplication with android_media_AudioTrack_native_setup() audio_stream_type_t nativeStreamType; @@ -786,7 +791,7 @@ static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobjec static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, jint sampleRateInHertz, jint nbChannels, jint audioFormat) { - int frameCount = 0; + size_t frameCount = 0; if (AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT, sampleRateInHertz) != NO_ERROR) { return -1; @@ -875,8 +880,6 @@ static JNINativeMethod gMethods[] = { #define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" #define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO" #define JAVA_CONST_STREAM_DTMF_NAME "STREAM_DTMF" -#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" -#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" #define JAVA_JNIDATA_FIELD_NAME "mJniData" @@ -940,17 +943,6 @@ int register_android_media_AudioTrack(JNIEnv *env) return -1; } - // Get the memory mode constants - if ( !android_media_getIntConstantFromClass(env, audioTrackClass, - kClassPathName, - JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC)) - || !android_media_getIntConstantFromClass(env, audioTrackClass, - kClassPathName, - JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) { - // error log performed in android_media_getIntConstantFromClass() - return -1; - } - // Get the format constants from the AudioFormat class jclass audioFormatClass = NULL; audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp index 5d24f61..80d13be 100644 --- a/core/jni/android_media_RemoteDisplay.cpp +++ b/core/jni/android_media_RemoteDisplay.cpp @@ -27,7 +27,7 @@ #include <binder/IServiceManager.h> -#include <gui/ISurfaceTexture.h> +#include <gui/IGraphicBufferProducer.h> #include <media/IMediaPlayerService.h> #include <media/IRemoteDisplay.h> @@ -60,14 +60,14 @@ protected: } public: - virtual void onDisplayConnected(const sp<ISurfaceTexture>& surfaceTexture, + virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer, uint32_t width, uint32_t height, uint32_t flags) { JNIEnv* env = AndroidRuntime::getJNIEnv(); - jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, surfaceTexture); + jobject surfaceObj = android_view_Surface_createFromIGraphicBufferProducer(env, bufferProducer); if (surfaceObj == NULL) { ALOGE("Could not create Surface from surface texture %p provided by media server.", - surfaceTexture.get()); + bufferProducer.get()); return; } diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp index de29b28..76e42bc 100644 --- a/core/jni/android_media_ToneGenerator.cpp +++ b/core/jni/android_media_ToneGenerator.cpp @@ -93,11 +93,6 @@ static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, ALOGV("android_media_ToneGenerator_native_setup jobject: %x", (int)thiz); - if (lpToneGen == NULL) { - ALOGE("ToneGenerator creation failed"); - jniThrowException(env, "java/lang/OutOfMemoryError", NULL); - return; - } ALOGV("ToneGenerator lpToneGen: %x", (unsigned int)lpToneGen); if (!lpToneGen->isInited()) { diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 1f2b1ae..f5f22b2 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -28,25 +28,25 @@ int ifc_enable(const char *ifname); int ifc_disable(const char *ifname); int ifc_reset_connections(const char *ifname, int reset_mask); -int dhcp_do_request(const char *ifname, +int dhcp_do_request(const char * const ifname, const char *ipaddr, const char *gateway, uint32_t *prefixLength, - const char *dns1, - const char *dns2, + const char *dns[], const char *server, uint32_t *lease, - const char *vendorInfo); + const char *vendorInfo, + const char *domains); -int dhcp_do_request_renew(const char *ifname, +int dhcp_do_request_renew(const char * const ifname, const char *ipaddr, const char *gateway, uint32_t *prefixLength, - const char *dns1, - const char *dns2, + const char *dns[], const char *server, uint32_t *lease, - const char *vendorInfo); + const char *vendorInfo, + const char *domains); int dhcp_stop(const char *ifname); int dhcp_release_lease(const char *ifname); @@ -63,15 +63,16 @@ namespace android { * to look them up every time. */ static struct fieldIds { - jmethodID constructorId; - jfieldID ipaddress; - jfieldID prefixLength; - jfieldID dns1; - jfieldID dns2; - jfieldID serverAddress; - jfieldID leaseDuration; - jfieldID vendorInfo; -} dhcpInfoInternalFieldIds; + jmethodID clear; + jmethodID setInterfaceName; + jmethodID addLinkAddress; + jmethodID addGateway; + jmethodID addDns; + jmethodID setDomains; + jmethodID setServerAddress; + jmethodID setLeaseDuration; + jmethodID setVendorInfo; +} dhcpResultsFieldIds; static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname) { @@ -109,7 +110,7 @@ static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, } static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname, - jobject info, bool renew) + jobject dhcpResults, bool renew) { int result; char ipaddr[PROPERTY_VALUE_MAX]; @@ -117,59 +118,87 @@ static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstr char gateway[PROPERTY_VALUE_MAX]; char dns1[PROPERTY_VALUE_MAX]; char dns2[PROPERTY_VALUE_MAX]; + char dns3[PROPERTY_VALUE_MAX]; + char dns4[PROPERTY_VALUE_MAX]; + const char *dns[5] = {dns1, dns2, dns3, dns4, NULL}; char server[PROPERTY_VALUE_MAX]; uint32_t lease; char vendorInfo[PROPERTY_VALUE_MAX]; + char domains[PROPERTY_VALUE_MAX]; const char *nameStr = env->GetStringUTFChars(ifname, NULL); if (nameStr == NULL) return (jboolean)false; if (renew) { result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength, - dns1, dns2, server, &lease, vendorInfo); + dns, server, &lease, vendorInfo, domains); } else { result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength, - dns1, dns2, server, &lease, vendorInfo); + dns, server, &lease, vendorInfo, domains); } - env->ReleaseStringUTFChars(ifname, nameStr); if (result == 0) { - env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr)); + env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.clear); + + // set mIfaceName + // dhcpResults->setInterfaceName(ifname) + env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setInterfaceName, ifname); + // set the linkAddress + // dhcpResults->addLinkAddress(inetAddress, prefixLength) + result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.addLinkAddress, + env->NewStringUTF(ipaddr), prefixLength); + } + + if (result == 0) { // set the gateway - jclass cls = env->FindClass("java/net/InetAddress"); - jmethodID method = env->GetStaticMethodID(cls, "getByName", - "(Ljava/lang/String;)Ljava/net/InetAddress;"); - jvalue args[1]; - args[0].l = env->NewStringUTF(gateway); - jobject inetAddressObject = env->CallStaticObjectMethodA(cls, method, args); - - if (!env->ExceptionOccurred()) { - cls = env->FindClass("android/net/RouteInfo"); - method = env->GetMethodID(cls, "<init>", "(Ljava/net/InetAddress;)V"); - args[0].l = inetAddressObject; - jobject routeInfoObject = env->NewObjectA(cls, method, args); - - cls = env->FindClass("android/net/DhcpInfoInternal"); - method = env->GetMethodID(cls, "addRoute", "(Landroid/net/RouteInfo;)V"); - args[0].l = routeInfoObject; - env->CallVoidMethodA(info, method, args); - } else { - // if we have an exception (host not found perhaps), just don't add the route - env->ExceptionClear(); + // dhcpResults->addGateway(gateway) + result = env->CallBooleanMethod(dhcpResults, + dhcpResultsFieldIds.addGateway, env->NewStringUTF(gateway)); + } + + if (result == 0) { + // dhcpResults->addDns(new InetAddress(dns1)) + result = env->CallBooleanMethod(dhcpResults, + dhcpResultsFieldIds.addDns, env->NewStringUTF(dns1)); + } + + if (result == 0) { + env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setDomains, + env->NewStringUTF(domains)); + + result = env->CallBooleanMethod(dhcpResults, + dhcpResultsFieldIds.addDns, env->NewStringUTF(dns2)); + + if (result == 0) { + result = env->CallBooleanMethod(dhcpResults, + dhcpResultsFieldIds.addDns, env->NewStringUTF(dns3)); + if (result == 0) { + result = env->CallBooleanMethod(dhcpResults, + dhcpResultsFieldIds.addDns, env->NewStringUTF(dns4)); + } } + } - env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength); - env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1)); - env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2)); - env->SetObjectField(info, dhcpInfoInternalFieldIds.serverAddress, + if (result == 0) { + // dhcpResults->setServerAddress(new InetAddress(server)) + result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.setServerAddress, env->NewStringUTF(server)); - env->SetIntField(info, dhcpInfoInternalFieldIds.leaseDuration, lease); - env->SetObjectField(info, dhcpInfoInternalFieldIds.vendorInfo, env->NewStringUTF(vendorInfo)); + } + + if (result == 0) { + // dhcpResults->setLeaseDuration(lease) + env->CallVoidMethod(dhcpResults, + dhcpResultsFieldIds.setLeaseDuration, lease); + + // dhcpResults->setVendorInfo(vendorInfo) + env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setVendorInfo, + env->NewStringUTF(vendorInfo)); } return (jboolean)(result == 0); } + static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info) { return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false); @@ -217,8 +246,8 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface }, { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface }, { "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections }, - { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp }, - { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcpRenew }, + { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_runDhcp }, + { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_runDhcpRenew }, { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp }, { "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease }, { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError }, @@ -226,16 +255,26 @@ static JNINativeMethod gNetworkUtilMethods[] = { int register_android_net_NetworkUtils(JNIEnv* env) { - jclass dhcpInfoInternalClass = env->FindClass("android/net/DhcpInfoInternal"); - LOG_FATAL_IF(dhcpInfoInternalClass == NULL, "Unable to find class android/net/DhcpInfoInternal"); - dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalClass, "<init>", "()V"); - dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;"); - dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalClass, "prefixLength", "I"); - dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalClass, "dns1", "Ljava/lang/String;"); - dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalClass, "dns2", "Ljava/lang/String;"); - dhcpInfoInternalFieldIds.serverAddress = env->GetFieldID(dhcpInfoInternalClass, "serverAddress", "Ljava/lang/String;"); - dhcpInfoInternalFieldIds.leaseDuration = env->GetFieldID(dhcpInfoInternalClass, "leaseDuration", "I"); - dhcpInfoInternalFieldIds.vendorInfo = env->GetFieldID(dhcpInfoInternalClass, "vendorInfo", "Ljava/lang/String;"); + jclass dhcpResultsClass = env->FindClass("android/net/DhcpResults"); + LOG_FATAL_IF(dhcpResultsClass == NULL, "Unable to find class android/net/DhcpResults"); + dhcpResultsFieldIds.clear = + env->GetMethodID(dhcpResultsClass, "clear", "()V"); + dhcpResultsFieldIds.setInterfaceName = + env->GetMethodID(dhcpResultsClass, "setInterfaceName", "(Ljava/lang/String;)V"); + dhcpResultsFieldIds.addLinkAddress = + env->GetMethodID(dhcpResultsClass, "addLinkAddress", "(Ljava/lang/String;I)Z"); + dhcpResultsFieldIds.addGateway = + env->GetMethodID(dhcpResultsClass, "addGateway", "(Ljava/lang/String;)Z"); + dhcpResultsFieldIds.addDns = + env->GetMethodID(dhcpResultsClass, "addDns", "(Ljava/lang/String;)Z"); + dhcpResultsFieldIds.setDomains = + env->GetMethodID(dhcpResultsClass, "setDomains", "(Ljava/lang/String;)V"); + dhcpResultsFieldIds.setServerAddress = + env->GetMethodID(dhcpResultsClass, "setServerAddress", "(Ljava/lang/String;)Z"); + dhcpResultsFieldIds.setLeaseDuration = + env->GetMethodID(dhcpResultsClass, "setLeaseDuration", "(I)V"); + dhcpResultsFieldIds.setVendorInfo = + env->GetMethodID(dhcpResultsClass, "setVendorInfo", "(Ljava/lang/String;)V"); return AndroidRuntime::registerNativeMethods(env, NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods)); diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp index 325fe26..0df8638 100644 --- a/core/jni/android_net_TrafficStats.cpp +++ b/core/jni/android_net_TrafficStats.cpp @@ -31,286 +31,162 @@ namespace android { -static const uint64_t VALUE_UNKNOWN = -1; -static const char* IFACE_STAT_ALL = "/proc/net/xt_qtaguid/iface_stat_all"; - -enum Tx_Rx { - TX, - RX -}; - -enum Tcp_Udp { - TCP, - UDP, - TCP_AND_UDP -}; +static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt"; +static const char* QTAGUID_UID_STATS = "/proc/net/xt_qtaguid/stats"; // NOTE: keep these in sync with TrafficStats.java -enum IfaceStatType { +static const uint64_t UNKNOWN = -1; + +enum StatsType { RX_BYTES = 0, RX_PACKETS = 1, TX_BYTES = 2, - TX_PACKETS = 3 + TX_PACKETS = 3, + TCP_RX_PACKETS = 4, + TCP_TX_PACKETS = 5 }; -struct IfaceStat { +struct Stats { uint64_t rxBytes; uint64_t rxPackets; uint64_t txBytes; uint64_t txPackets; + uint64_t tcpRxPackets; + uint64_t tcpTxPackets; }; -// Returns an ASCII decimal number read from the specified file, -1 on error. -static jlong readNumber(char const* filename) { - char buf[80]; - int fd = open(filename, O_RDONLY); - if (fd < 0) { - if (errno != ENOENT) ALOGE("Can't open %s: %s", filename, strerror(errno)); - return -1; - } - - int len = read(fd, buf, sizeof(buf) - 1); - if (len < 0) { - ALOGE("Can't read %s: %s", filename, strerror(errno)); - close(fd); - return -1; +static uint64_t getStatsType(struct Stats* stats, StatsType type) { + switch (type) { + case RX_BYTES: + return stats->rxBytes; + case RX_PACKETS: + return stats->rxPackets; + case TX_BYTES: + return stats->txBytes; + case TX_PACKETS: + return stats->txPackets; + case TCP_RX_PACKETS: + return stats->tcpRxPackets; + case TCP_TX_PACKETS: + return stats->tcpTxPackets; + default: + return UNKNOWN; } - - close(fd); - buf[len] = '\0'; - return atoll(buf); } -static int parseIfaceStat(const char* iface, struct IfaceStat* stat) { - FILE *fp = fopen(IFACE_STAT_ALL, "r"); - if (!fp) { - return errno; +static int parseIfaceStats(const char* iface, struct Stats* stats) { + FILE *fp = fopen(QTAGUID_IFACE_STATS, "r"); + if (fp == NULL) { + return -1; } - char buffer[256]; + char buffer[384]; char cur_iface[32]; - int active; - uint64_t rxBytes, rxPackets, txBytes, txPackets, devRxBytes, devRxPackets, devTxBytes, - devTxPackets; - - while (fgets(buffer, 256, fp) != NULL) { - if (sscanf(buffer, "%31s %d %llu %llu %llu %llu %llu %llu %llu %llu", cur_iface, &active, - &rxBytes, &rxPackets, &txBytes, &txPackets, &devRxBytes, &devRxPackets, - &devTxBytes, &devTxPackets) != 10) { - continue; - } - - if (!iface || !strcmp(iface, cur_iface)) { - stat->rxBytes += rxBytes; - stat->rxPackets += rxPackets; - stat->txBytes += txBytes; - stat->txPackets += txPackets; - - if (active) { - stat->rxBytes += devRxBytes; - stat->rxPackets += devRxPackets; - stat->txBytes += devTxBytes; - stat->txPackets += devTxPackets; + bool foundTcp = false; + uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets; + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + int matched = sscanf(buffer, "%31s %llu %llu %llu %llu " + "%*u %llu %*u %*u %*u %*u " + "%*u %llu %*u %*u %*u %*u", cur_iface, &rxBytes, + &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets); + if (matched >= 5) { + if (matched == 7) { + foundTcp = true; + } + if (!iface || !strcmp(iface, cur_iface)) { + stats->rxBytes += rxBytes; + stats->rxPackets += rxPackets; + stats->txBytes += txBytes; + stats->txPackets += txPackets; + if (matched == 7) { + stats->tcpRxPackets += tcpRxPackets; + stats->tcpTxPackets += tcpTxPackets; + } } } } - fclose(fp); + if (!foundTcp) { + stats->tcpRxPackets = UNKNOWN; + stats->tcpTxPackets = UNKNOWN; + } + + if (fclose(fp) != 0) { + return -1; + } return 0; } -static uint64_t getIfaceStatType(const char* iface, IfaceStatType type) { - struct IfaceStat stat; - memset(&stat, 0, sizeof(IfaceStat)); +static int parseUidStats(const uint32_t uid, struct Stats* stats) { + FILE *fp = fopen(QTAGUID_UID_STATS, "r"); + if (fp == NULL) { + return -1; + } - if (parseIfaceStat(iface, &stat)) { - return VALUE_UNKNOWN; + char buffer[384]; + char iface[32]; + uint32_t idx, cur_uid, set; + uint64_t tag, rxBytes, rxPackets, txBytes, txPackets; + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &idx, + iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, &txBytes, + &txPackets) == 9) { + if (uid == cur_uid && tag == 0L) { + stats->rxBytes += rxBytes; + stats->rxPackets += rxPackets; + stats->txBytes += txBytes; + stats->txPackets += txPackets; + } + } } - switch (type) { - case RX_BYTES: - return stat.rxBytes; - case RX_PACKETS: - return stat.rxPackets; - case TX_BYTES: - return stat.txBytes; - case TX_PACKETS: - return stat.txPackets; - default: - return VALUE_UNKNOWN; + if (fclose(fp) != 0) { + return -1; } + return 0; } static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) { - return getIfaceStatType(NULL, (IfaceStatType) type); -} - -static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) { - struct IfaceStat stat; - const char* ifaceChars = env->GetStringUTFChars(iface, NULL); - if (ifaceChars) { - uint64_t stat = getIfaceStatType(ifaceChars, (IfaceStatType) type); - env->ReleaseStringUTFChars(iface, ifaceChars); - return stat; + struct Stats stats; + memset(&stats, 0, sizeof(Stats)); + if (parseIfaceStats(NULL, &stats) == 0) { + return getStatsType(&stats, (StatsType) type); } else { - return VALUE_UNKNOWN; + return UNKNOWN; } } - -// Per-UID stats require reading from a constructed filename. - -static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid, - enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { - char tcp_filename[80], udp_filename[80]; - jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1; - - switch (tx_or_rx) { - case TX: - sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid); - sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid); - break; - case RX: - sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid); - sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid); - break; - default: - return -1; +static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) { + ScopedUtfChars iface8(env, iface); + if (iface8.c_str() == NULL) { + return UNKNOWN; } - switch (tcp_or_udp) { - case TCP: - tcp_bytes = readNumber(tcp_filename); - total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1; - break; - case UDP: - udp_bytes = readNumber(udp_filename); - total_bytes = (udp_bytes >= 0) ? udp_bytes : -1; - break; - case TCP_AND_UDP: - tcp_bytes = readNumber(tcp_filename); - total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0); - - udp_bytes = readNumber(udp_filename); - total_bytes += (udp_bytes >= 0 ? udp_bytes : 0); - break; - default: - return -1; + struct Stats stats; + memset(&stats, 0, sizeof(Stats)); + if (parseIfaceStats(NULL, &stats) == 0) { + return getStatsType(&stats, (StatsType) type); + } else { + return UNKNOWN; } - - return total_bytes; } -static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid, - enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { - char tcp_filename[80], udp_filename[80]; - jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1; - - switch (tx_or_rx) { - case TX: - sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid); - sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid); - break; - case RX: - sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid); - sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid); - break; - default: - return -1; - } - - switch (tcp_or_udp) { - case TCP: - tcp_pkts = readNumber(tcp_filename); - total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1; - break; - case UDP: - udp_pkts = readNumber(udp_filename); - total_pkts = (udp_pkts >= 0) ? udp_pkts : -1; - break; - case TCP_AND_UDP: - tcp_pkts = readNumber(tcp_filename); - total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0); - - udp_pkts = readNumber(udp_filename); - total_pkts += (udp_pkts >= 0 ? udp_pkts : 0); - break; - default: - return -1; +static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) { + struct Stats stats; + memset(&stats, 0, sizeof(Stats)); + if (parseUidStats(uid, &stats) == 0) { + return getStatsType(&stats, (StatsType) type); + } else { + return UNKNOWN; } - - return total_pkts; -} - -static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) { - return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP); -} - -static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) { - return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP); -} - -/* TCP Segments + UDP Packets */ -static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) { - return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP); -} - -/* TCP Segments + UDP Packets */ -static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) { - return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP); -} - -static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) { - return getUidBytes(env, clazz, uid, TX, TCP); -} - -static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) { - return getUidBytes(env, clazz, uid, RX, TCP); -} - -static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) { - return getUidBytes(env, clazz, uid, TX, UDP); -} - -static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) { - return getUidBytes(env, clazz, uid, RX, UDP); -} - -static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) { - return getUidPkts(env, clazz, uid, TX, TCP); -} - -static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) { - return getUidPkts(env, clazz, uid, RX, TCP); -} - -static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) { - return getUidPkts(env, clazz, uid, TX, UDP); -} - -static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) { - return getUidPkts(env, clazz, uid, RX, UDP); } static JNINativeMethod gMethods[] = { {"nativeGetTotalStat", "(I)J", (void*) getTotalStat}, {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat}, - - /* Per-UID Stats */ - {"getUidTxBytes", "(I)J", (void*) getUidTxBytes}, - {"getUidRxBytes", "(I)J", (void*) getUidRxBytes}, - {"getUidTxPackets", "(I)J", (void*) getUidTxPackets}, - {"getUidRxPackets", "(I)J", (void*) getUidRxPackets}, - - {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes}, - {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes}, - {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes}, - {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes}, - - {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments}, - {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments}, - {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets}, - {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets}, + {"nativeGetUidStat", "(II)J", (void*) getUidStat}, }; int register_android_net_TrafficStats(JNIEnv* env) { diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp index b1664c6..26fc261 100644 --- a/core/jni/android_opengl_EGL14.cpp +++ b/core/jni/android_opengl_EGL14.cpp @@ -28,8 +28,8 @@ #include <EGL/egl.h> #include <gui/Surface.h> -#include <gui/SurfaceTexture.h> -#include <gui/SurfaceTextureClient.h> +#include <gui/GLConsumer.h> +#include <gui/Surface.h> #include <ui/ANativeObjectBase.h> @@ -605,7 +605,7 @@ android_eglCreateWindowSurfaceTexture jint _remaining; EGLint *attrib_list = (EGLint *) 0; android::sp<ANativeWindow> window; - android::sp<android::SurfaceTexture> surfaceTexture; + android::sp<android::GLConsumer> surfaceTexture; if (!attrib_list_ref) { _exception = 1; @@ -627,7 +627,11 @@ not_valid_surface: goto exit; } surfaceTexture = android::SurfaceTexture_getSurfaceTexture(_env, win); - window = new android::SurfaceTextureClient(surfaceTexture); + + if (surfaceTexture == NULL) + goto not_valid_surface; + + window = new android::Surface(surfaceTexture->getBufferQueue()); if (window == NULL) goto not_valid_surface; diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index a4dcac6..7540645 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -109,55 +109,34 @@ void NativeMessageQueue::wake() { // ---------------------------------------------------------------------------- -static NativeMessageQueue* android_os_MessageQueue_getNativeMessageQueue(JNIEnv* env, - jobject messageQueueObj) { +sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) { jint intPtr = env->GetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr); return reinterpret_cast<NativeMessageQueue*>(intPtr); } -static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, - NativeMessageQueue* nativeMessageQueue) { - env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, - reinterpret_cast<jint>(nativeMessageQueue)); -} - -sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) { - NativeMessageQueue* nativeMessageQueue = - android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj); - return nativeMessageQueue; -} - -static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { +static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); - return; + return 0; } nativeMessageQueue->incStrong(env); - android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue); + return reinterpret_cast<jint>(nativeMessageQueue); } -static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jobject obj) { - NativeMessageQueue* nativeMessageQueue = - android_os_MessageQueue_getNativeMessageQueue(env, obj); - if (nativeMessageQueue) { - android_os_MessageQueue_setNativeMessageQueue(env, obj, NULL); - nativeMessageQueue->decStrong(env); - } -} - -static void throwQueueNotInitialized(JNIEnv* env) { - jniThrowException(env, "java/lang/IllegalStateException", "Message queue not initialized"); +static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) { + NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); + nativeMessageQueue->decStrong(env); } -static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, +static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz, jint ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, timeoutMillis); } -static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) { +static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); return nativeMessageQueue->wake(); } @@ -166,8 +145,8 @@ static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint pt static JNINativeMethod gMessageQueueMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit }, - { "nativeDestroy", "()V", (void*)android_os_MessageQueue_nativeDestroy }, + { "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit }, + { "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy }, { "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce }, { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake } }; diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp index af8edae..f028c86 100644 --- a/core/jni/android_os_Trace.cpp +++ b/core/jni/android_os_Trace.cpp @@ -19,30 +19,30 @@ #include <JNIHelp.h> #include <ScopedUtfChars.h> -#include <utils/Trace.h> +#include <cutils/trace.h> #include <cutils/log.h> namespace android { static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv* env, jclass clazz) { - return Tracer::getEnabledTags(); + return atrace_get_enabled_tags(); } static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass clazz, jlong tag, jstring nameStr, jint value) { ScopedUtfChars name(env, nameStr); - Tracer::traceCounter(tag, name.c_str(), value); + atrace_int(tag, name.c_str(), value); } static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz, jlong tag, jstring nameStr) { ScopedUtfChars name(env, nameStr); - Tracer::traceBegin(tag, name.c_str()); + atrace_begin(tag, name.c_str()); } static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz, jlong tag) { - Tracer::traceEnd(tag); + atrace_end(tag); } static JNINativeMethod gTraceMethods[] = { diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d422951..785bf13 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -685,6 +685,10 @@ static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject jobject outValue, jboolean resolve) { + if (outValue == NULL) { + jniThrowNullPointerException(env, "outValue"); + return NULL; + } AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return 0; diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 0290857..5d32328 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -268,6 +268,15 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin closedir(d); } +jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid) +{ + SchedPolicy sp; + if (get_sched_policy(pid, &sp) != 0) { + signalExceptionForGroupError(env, errno); + } + return (int) sp; +} + static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) { // Establishes the calling thread as illegal to put into the background. // Typically used only for the system process's main looper. @@ -991,7 +1000,8 @@ static const JNINativeMethod methods[] = { {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, - {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, + {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, + {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, {"setOomAdj", "(II)Z", (void*)android_os_Process_setOomAdj}, {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, {"setUid", "(I)I", (void*)android_os_Process_setUid}, diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 5d306d2..a6bb7c7 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -24,7 +24,7 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_graphics_SurfaceTexture.h> -#include <gui/SurfaceTexture.h> +#include <gui/GLConsumer.h> #include <androidfw/ResourceTypes.h> @@ -41,6 +41,7 @@ #include <SkTemplates.h> #include <SkXfermode.h> +#include <DisplayList.h> #include <DisplayListRenderer.h> #include <LayerRenderer.h> #include <OpenGLRenderer.h> @@ -156,6 +157,17 @@ static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) return Stencil::getStencilSize(); } +static void android_view_GLES20Canvas_setName(JNIEnv* env, + jobject clazz, OpenGLRenderer* renderer, jstring name) { + if (name != NULL) { + const char* textArray = env->GetStringUTFChars(name, NULL); + renderer->setName(textArray); + env->ReleaseStringUTFChars(name, textArray); + } else { + renderer->setName(NULL); + } +} + // ---------------------------------------------------------------------------- // Functor // ---------------------------------------------------------------------------- @@ -275,6 +287,16 @@ static bool android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject clazz, return renderer->clipRect(float(left), float(top), float(right), float(bottom), op); } +static bool android_view_GLES20Canvas_clipPath(JNIEnv* env, jobject clazz, + OpenGLRenderer* renderer, SkPath* path, SkRegion::Op op) { + return renderer->clipPath(path, op); +} + +static bool android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz, + OpenGLRenderer* renderer, SkRegion* region, SkRegion::Op op) { + return renderer->clipRegion(region, op); +} + static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jobject rect) { const android::uirenderer::Rect& bounds(renderer->getClipBounds()); @@ -449,16 +471,40 @@ static void android_view_GLES20Canvas_drawArc(JNIEnv* env, jobject clazz, renderer->drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint); } -static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz, +static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) { - SkRegion::Iterator it(*region); - while (!it.done()) { - const SkIRect& r = it.rect(); - renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); - it.next(); + if (paint->getStyle() != SkPaint::kFill_Style || + (paint->isAntiAlias() && !renderer->isCurrentTransformSimple())) { + SkRegion::Iterator it(*region); + while (!it.done()) { + const SkIRect& r = it.rect(); + renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); + it.next(); + } + } else { + int count = 0; + Vector<float> rects; + SkRegion::Iterator it(*region); + while (!it.done()) { + const SkIRect& r = it.rect(); + rects.push(r.fLeft); + rects.push(r.fTop); + rects.push(r.fRight); + rects.push(r.fBottom); + count += 4; + it.next(); + } + renderer->drawRects(rects.array(), count, paint); } } +static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz, + OpenGLRenderer* renderer, jfloatArray rects, jint count, SkPaint* paint) { + jfloat* storage = env->GetFloatArrayElements(rects, NULL); + renderer->drawRects(storage, count, paint); + env->ReleaseFloatArrayElements(rects, storage, 0); +} + static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) { jfloat* storage = env->GetFloatArrayElements(points, NULL); @@ -523,9 +569,9 @@ static void android_view_GLES20Canvas_resetPaintFilter(JNIEnv* env, jobject claz // ---------------------------------------------------------------------------- static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, count, count, flags); + text, 0, count, count); if (value == NULL) { return; } @@ -539,9 +585,9 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, } static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count, - SkPath* path, jfloat hOffset, jfloat vOffset, int flags, SkPaint* paint) { + SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, count, count, flags); + text, 0, count, count); if (value == NULL) { return; } @@ -554,9 +600,9 @@ static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int co static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, jint start, jint count, jint contextCount, jfloat x, jfloat y, - int flags, SkPaint* paint) { + SkPaint* paint) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, start, count, contextCount, flags); + text, start, count, contextCount); if (value == NULL) { return; } @@ -571,64 +617,62 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jcharArray text, jint index, jint count, - jfloat x, jfloat y, jint flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); - renderText(renderer, textArray + index, count, x, y, flags, paint); + renderText(renderer, textArray + index, count, x, y, paint); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jstring text, jint start, jint end, - jfloat x, jfloat y, jint flags, SkPaint* paint) { + jfloat x, jfloat y, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); - renderText(renderer, textArray + start, end - start, x, y, flags, paint); + renderText(renderer, textArray + start, end - start, x, y, paint); env->ReleaseStringChars(text, textArray); } static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jcharArray text, jint index, jint count, - SkPath* path, jfloat hOffset, jfloat vOffset, jint flags, SkPaint* paint) { + SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); renderTextOnPath(renderer, textArray + index, count, path, - hOffset, vOffset, flags, paint); + hOffset, vOffset, paint); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jstring text, jint start, jint end, - SkPath* path, jfloat hOffset, jfloat vOffset, jint flags, SkPaint* paint) { + SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); renderTextOnPath(renderer, textArray + start, end - start, path, - hOffset, vOffset, flags, paint); + hOffset, vOffset, paint); env->ReleaseStringChars(text, textArray); } static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jcharArray text, jint index, jint count, - jint contextIndex, jint contextCount, jfloat x, jfloat y, jint dirFlags, - SkPaint* paint) { + jint contextIndex, jint contextCount, jfloat x, jfloat y, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); renderTextRun(renderer, textArray + contextIndex, index - contextIndex, - count, contextCount, x, y, dirFlags, paint); + count, contextCount, x, y, paint); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jstring text, jint start, jint end, - jint contextStart, int contextEnd, jfloat x, jfloat y, jint dirFlags, - SkPaint* paint) { + jint contextStart, int contextEnd, jfloat x, jfloat y, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); jint count = end - start; jint contextCount = contextEnd - contextStart; renderTextRun(renderer, textArray + contextStart, start - contextStart, - count, contextCount, x, y, dirFlags, paint); + count, contextCount, x, y, paint); env->ReleaseStringChars(text, textArray); } static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count, - const jfloat* positions, jint dirFlags, SkPaint* paint) { + const jfloat* positions, SkPaint* paint) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, count, count, dirFlags); + text, 0, count, count); if (value == NULL) { return; } @@ -646,7 +690,7 @@ static void android_view_GLES20Canvas_drawPosTextArray(JNIEnv* env, jobject claz jchar* textArray = env->GetCharArrayElements(text, NULL); jfloat* positions = env->GetFloatArrayElements(pos, NULL); - renderPosText(renderer, textArray + index, count, positions, kBidi_LTR, paint); + renderPosText(renderer, textArray + index, count, positions, paint); env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); @@ -658,7 +702,7 @@ static void android_view_GLES20Canvas_drawPosText(JNIEnv* env, jobject clazz, const jchar* textArray = env->GetStringChars(text, NULL); jfloat* positions = env->GetFloatArrayElements(pos, NULL); - renderPosText(renderer, textArray + start, end - start, positions, kBidi_LTR, paint); + renderPosText(renderer, textArray + start, end - start, positions, paint); env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT); env->ReleaseStringChars(text, textArray); @@ -673,20 +717,6 @@ static DisplayList* android_view_GLES20Canvas_getDisplayList(JNIEnv* env, return renderer->getDisplayList(displayList); } -static jint android_view_GLES20Canvas_getDisplayListSize(JNIEnv* env, - jobject clazz, DisplayList* displayList) { - return displayList->getSize(); -} - -static void android_view_GLES20Canvas_setDisplayListName(JNIEnv* env, - jobject clazz, DisplayList* displayList, jstring name) { - if (name != NULL) { - const char* textArray = env->GetStringUTFChars(name, NULL); - displayList->setName(textArray); - env->ReleaseStringUTFChars(name, textArray); - } -} - static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) { return new DisplayListRenderer; @@ -697,11 +727,6 @@ static void android_view_GLES20Canvas_resetDisplayListRenderer(JNIEnv* env, renderer->reset(); } -static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, - jobject clazz, DisplayList* displayList) { - DisplayList::destroyDisplayListDeferred(displayList); -} - static jint android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList, jobject dirty, jint flags) { @@ -806,7 +831,7 @@ static void android_view_GLES20Canvas_setOpaqueLayer(JNIEnv* env, jobject clazz, static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz, Layer* layer, jint width, jint height, jboolean isOpaque, jobject surface) { float transform[16]; - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); if (surfaceTexture->updateTexImage() == NO_ERROR) { surfaceTexture->getTransformMatrix(transform); @@ -915,6 +940,8 @@ static JNINativeMethod gMethods[] = { { "nPrepare", "(IZ)I", (void*) android_view_GLES20Canvas_prepare }, { "nPrepareDirty", "(IIIIIZ)I", (void*) android_view_GLES20Canvas_prepareDirty }, { "nFinish", "(I)V", (void*) android_view_GLES20Canvas_finish }, + { "nSetName", "(ILjava/lang/String;)V", + (void*) android_view_GLES20Canvas_setName }, { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize }, @@ -937,6 +964,8 @@ static JNINativeMethod gMethods[] = { { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject }, { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF }, { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect }, + { "nClipPath", "(III)Z", (void*) android_view_GLES20Canvas_clipPath }, + { "nClipRegion", "(III)Z", (void*) android_view_GLES20Canvas_clipRegion }, { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate }, { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate }, @@ -958,7 +987,8 @@ static JNINativeMethod gMethods[] = { { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, - { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects }, + { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRegionAsRects }, + { "nDrawRects", "(I[FII)V", (void*) android_view_GLES20Canvas_drawRects }, { "nDrawRoundRect", "(IFFFFFFI)V", (void*) android_view_GLES20Canvas_drawRoundRect }, { "nDrawCircle", "(IFFFI)V", (void*) android_view_GLES20Canvas_drawCircle }, { "nDrawOval", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawOval }, @@ -976,16 +1006,16 @@ static JNINativeMethod gMethods[] = { { "nSetupPaintFilter", "(III)V", (void*) android_view_GLES20Canvas_setupPaintFilter }, { "nResetPaintFilter", "(I)V", (void*) android_view_GLES20Canvas_resetPaintFilter }, - { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray }, - { "nDrawText", "(ILjava/lang/String;IIFFII)V", + { "nDrawText", "(I[CIIFFI)V", (void*) android_view_GLES20Canvas_drawTextArray }, + { "nDrawText", "(ILjava/lang/String;IIFFI)V", (void*) android_view_GLES20Canvas_drawText }, - { "nDrawTextOnPath", "(I[CIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, - { "nDrawTextOnPath", "(ILjava/lang/String;IIIFFII)V", + { "nDrawTextOnPath", "(I[CIIIFFI)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, + { "nDrawTextOnPath", "(ILjava/lang/String;IIIFFI)V", (void*) android_view_GLES20Canvas_drawTextOnPath }, - { "nDrawTextRun", "(I[CIIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, - { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V", + { "nDrawTextRun", "(I[CIIIIFFI)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, + { "nDrawTextRun", "(ILjava/lang/String;IIIIFFI)V", (void*) android_view_GLES20Canvas_drawTextRun }, { "nDrawPosText", "(I[CII[FI)V", (void*) android_view_GLES20Canvas_drawPosTextArray }, @@ -996,17 +1026,13 @@ static JNINativeMethod gMethods[] = { (void*) android_view_GLES20Canvas_getClipBounds }, { "nGetDisplayList", "(II)I", (void*) android_view_GLES20Canvas_getDisplayList }, - { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, - { "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize }, - { "nSetDisplayListName", "(ILjava/lang/String;)V", - (void*) android_view_GLES20Canvas_setDisplayListName }, + { "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList }, { "nDrawDisplayList", "(IILandroid/graphics/Rect;I)I", (void*) android_view_GLES20Canvas_drawDisplayList }, { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer }, - { "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList }, { "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt }, { "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume }, diff --git a/core/jni/android_view_GLES20DisplayList.cpp b/core/jni/android_view_GLES20DisplayList.cpp index c5f52df..f7a5302 100644 --- a/core/jni/android_view_GLES20DisplayList.cpp +++ b/core/jni/android_view_GLES20DisplayList.cpp @@ -23,6 +23,7 @@ #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <DisplayList.h> #include <DisplayListRenderer.h> namespace android { @@ -36,11 +37,34 @@ using namespace uirenderer; */ #ifdef USE_OPENGL_RENDERER +// ---------------------------------------------------------------------------- +// DisplayList view properties +// ---------------------------------------------------------------------------- + static void android_view_GLES20DisplayList_reset(JNIEnv* env, jobject clazz, DisplayList* displayList) { displayList->reset(); } +static jint android_view_GLES20DisplayList_getDisplayListSize(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getSize(); +} + +static void android_view_GLES20DisplayList_setDisplayListName(JNIEnv* env, + jobject clazz, DisplayList* displayList, jstring name) { + if (name != NULL) { + const char* textArray = env->GetStringUTFChars(name, NULL); + displayList->setName(textArray); + env->ReleaseStringUTFChars(name, textArray); + } +} + +static void android_view_GLES20DisplayList_destroyDisplayList(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + DisplayList::destroyDisplayListDeferred(displayList); +} + // ---------------------------------------------------------------------------- // DisplayList view properties // ---------------------------------------------------------------------------- @@ -159,27 +183,112 @@ static void android_view_GLES20DisplayList_setBottom(JNIEnv* env, displayList->setBottom(bottom); } -static void android_view_GLES20DisplayList_setLeftTop(JNIEnv* env, - jobject clazz, DisplayList* displayList, int left, int top) { - displayList->setLeftTop(left, top); -} - static void android_view_GLES20DisplayList_setLeftTopRightBottom(JNIEnv* env, jobject clazz, DisplayList* displayList, int left, int top, int right, int bottom) { displayList->setLeftTopRightBottom(left, top, right, bottom); } -static void android_view_GLES20DisplayList_offsetLeftRight(JNIEnv* env, - jobject clazz, DisplayList* displayList, int offset) { +static void android_view_GLES20DisplayList_offsetLeftAndRight(JNIEnv* env, + jobject clazz, DisplayList* displayList, float offset) { displayList->offsetLeftRight(offset); } -static void android_view_GLES20DisplayList_offsetTopBottom(JNIEnv* env, - jobject clazz, DisplayList* displayList, int offset) { +static void android_view_GLES20DisplayList_offsetTopAndBottom(JNIEnv* env, + jobject clazz, DisplayList* displayList, float offset) { displayList->offsetTopBottom(offset); } +static void android_view_GLES20DisplayList_getMatrix(JNIEnv* env, + jobject clazz, DisplayList* displayList, SkMatrix* matrix) { + SkMatrix* source = displayList->getStaticMatrix(); + if (source) { + matrix->setConcat(SkMatrix::I(), *source); + } else { + matrix->setIdentity(); + } +} + +static jboolean android_view_GLES20DisplayList_hasOverlappingRendering(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->hasOverlappingRendering(); +} + +static jfloat android_view_GLES20DisplayList_getAlpha(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getAlpha(); +} + +static jfloat android_view_GLES20DisplayList_getLeft(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getLeft(); +} + +static jfloat android_view_GLES20DisplayList_getTop(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getTop(); +} + +static jfloat android_view_GLES20DisplayList_getRight(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getRight(); +} + +static jfloat android_view_GLES20DisplayList_getBottom(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getBottom(); +} + +static jfloat android_view_GLES20DisplayList_getCameraDistance(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getCameraDistance(); +} + +static jfloat android_view_GLES20DisplayList_getScaleX(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getScaleX(); +} + +static jfloat android_view_GLES20DisplayList_getScaleY(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getScaleY(); +} + +static jfloat android_view_GLES20DisplayList_getTranslationX(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getTranslationX(); +} + +static jfloat android_view_GLES20DisplayList_getTranslationY(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getTranslationY(); +} + +static jfloat android_view_GLES20DisplayList_getRotation(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getRotation(); +} + +static jfloat android_view_GLES20DisplayList_getRotationX(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getRotationX(); +} + +static jfloat android_view_GLES20DisplayList_getRotationY(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getRotationY(); +} + +static jfloat android_view_GLES20DisplayList_getPivotX(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getPivotX(); +} + +static jfloat android_view_GLES20DisplayList_getPivotY(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getPivotY(); +} + #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -190,6 +299,11 @@ const char* const kClassPathName = "android/view/GLES20DisplayList"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER + { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20DisplayList_destroyDisplayList }, + { "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20DisplayList_getDisplayListSize }, + { "nSetDisplayListName", "(ILjava/lang/String;)V", + (void*) android_view_GLES20DisplayList_setDisplayListName }, + { "nReset", "(I)V", (void*) android_view_GLES20DisplayList_reset }, { "nSetCaching", "(IZ)V", (void*) android_view_GLES20DisplayList_setCaching }, { "nSetStaticMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setStaticMatrix }, @@ -214,12 +328,29 @@ static JNINativeMethod gMethods[] = { { "nSetTop", "(II)V", (void*) android_view_GLES20DisplayList_setTop }, { "nSetRight", "(II)V", (void*) android_view_GLES20DisplayList_setRight }, { "nSetBottom", "(II)V", (void*) android_view_GLES20DisplayList_setBottom }, - { "nSetLeftTop", "(III)V", (void*) android_view_GLES20DisplayList_setLeftTop }, { "nSetLeftTopRightBottom","(IIIII)V", (void*) android_view_GLES20DisplayList_setLeftTopRightBottom }, - { "nOffsetLeftRight", "(II)V", (void*) android_view_GLES20DisplayList_offsetLeftRight }, - { "nOffsetTopBottom", "(II)V", (void*) android_view_GLES20DisplayList_offsetTopBottom }, - + { "nOffsetLeftAndRight", "(IF)V", (void*) android_view_GLES20DisplayList_offsetLeftAndRight }, + { "nOffsetTopAndBottom", "(IF)V", (void*) android_view_GLES20DisplayList_offsetTopAndBottom }, + + + { "nGetMatrix", "(II)V", (void*) android_view_GLES20DisplayList_getMatrix }, + { "nHasOverlappingRendering", "(I)Z", (void*) android_view_GLES20DisplayList_hasOverlappingRendering }, + { "nGetAlpha", "(I)F", (void*) android_view_GLES20DisplayList_getAlpha }, + { "nGetLeft", "(I)F", (void*) android_view_GLES20DisplayList_getLeft }, + { "nGetTop", "(I)F", (void*) android_view_GLES20DisplayList_getTop }, + { "nGetRight", "(I)F", (void*) android_view_GLES20DisplayList_getRight }, + { "nGetBottom", "(I)F", (void*) android_view_GLES20DisplayList_getBottom }, + { "nGetCameraDistance", "(I)F", (void*) android_view_GLES20DisplayList_getCameraDistance }, + { "nGetScaleX", "(I)F", (void*) android_view_GLES20DisplayList_getScaleX }, + { "nGetScaleY", "(I)F", (void*) android_view_GLES20DisplayList_getScaleY }, + { "nGetTranslationX", "(I)F", (void*) android_view_GLES20DisplayList_getTranslationX }, + { "nGetTranslationY", "(I)F", (void*) android_view_GLES20DisplayList_getTranslationY }, + { "nGetRotation", "(I)F", (void*) android_view_GLES20DisplayList_getRotation }, + { "nGetRotationX", "(I)F", (void*) android_view_GLES20DisplayList_getRotationX }, + { "nGetRotationY", "(I)F", (void*) android_view_GLES20DisplayList_getRotationY }, + { "nGetPivotX", "(I)F", (void*) android_view_GLES20DisplayList_getPivotX }, + { "nGetPivotY", "(I)F", (void*) android_view_GLES20DisplayList_getPivotY }, #endif }; diff --git a/core/jni/android_view_HardwareRenderer.cpp b/core/jni/android_view_HardwareRenderer.cpp index fa83170..948a966 100644 --- a/core/jni/android_view_HardwareRenderer.cpp +++ b/core/jni/android_view_HardwareRenderer.cpp @@ -22,6 +22,8 @@ #include <EGL/egl_cache.h> +#include <Caches.h> + #ifdef USE_OPENGL_RENDERER EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); #endif @@ -84,22 +86,17 @@ static jboolean android_view_HardwareRenderer_isBackBufferPreserved(JNIEnv* env, return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED; } -static void android_view_HardwareRenderer_disableVsync(JNIEnv* env, jobject clazz) { - EGLDisplay display = eglGetCurrentDisplay(); - - eglGetError(); - eglSwapInterval(display, 0); - - EGLint error = eglGetError(); - if (error != EGL_SUCCESS) { - RENDERER_LOGD("Could not disable v-sync (%x)", error); - } -} - // ---------------------------------------------------------------------------- // Tracing and debugging // ---------------------------------------------------------------------------- +static bool android_view_HardwareRenderer_loadProperties(JNIEnv* env, jobject clazz) { + if (uirenderer::Caches::hasInstance()) { + return uirenderer::Caches::getInstance().initProperties(); + } + return false; +} + static void android_view_HardwareRenderer_beginFrame(JNIEnv* env, jobject clazz, jintArray size) { @@ -146,7 +143,7 @@ static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER { "nIsBackBufferPreserved", "()Z", (void*) android_view_HardwareRenderer_isBackBufferPreserved }, { "nPreserveBackBuffer", "()Z", (void*) android_view_HardwareRenderer_preserveBackBuffer }, - { "nDisableVsync", "()V", (void*) android_view_HardwareRenderer_disableVsync }, + { "nLoadProperties", "()Z", (void*) android_view_HardwareRenderer_loadProperties }, { "nBeginFrame", "([I)V", (void*) android_view_HardwareRenderer_beginFrame }, #endif diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 2767e94..ab0d38e 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -18,41 +18,31 @@ #include <stdio.h> +#include "jni.h" +#include "JNIHelp.h" #include "android_os_Parcel.h" -#include "android_util_Binder.h" #include "android/graphics/GraphicsJNI.h" -#include "android/graphics/Region.h" -#include <binder/IMemory.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_view_Surface.h> +#include <android_runtime/android_graphics_SurfaceTexture.h> -#include <gui/ISurfaceComposer.h> #include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> -#include <gui/SurfaceTexture.h> +#include <gui/SurfaceControl.h> +#include <gui/GLConsumer.h> -#include <ui/DisplayInfo.h> #include <ui/Rect.h> #include <ui/Region.h> -#include <EGL/egl.h> - #include <SkCanvas.h> #include <SkBitmap.h> #include <SkRegion.h> -#include <SkPixelRef.h> -#include "jni.h" -#include "JNIHelp.h" -#include <android_runtime/AndroidRuntime.h> -#include <android_runtime/android_view_Surface.h> -#include <android_runtime/android_view_SurfaceSession.h> -#include <android_runtime/android_graphics_SurfaceTexture.h> #include <utils/misc.h> #include <utils/Log.h> #include <ScopedUtfChars.h> - // ---------------------------------------------------------------------------- namespace android { @@ -62,8 +52,7 @@ static const char* const OutOfResourcesException = static struct { jclass clazz; - jfieldID mNativeSurface; - jfieldID mNativeSurfaceControl; + jfieldID mNativeObject; jfieldID mGenerationId; jfieldID mCanvas; jfieldID mCanvasSaveCount; @@ -82,278 +71,96 @@ static struct { jfieldID mSurfaceFormat; } gCanvasClassInfo; -static struct { - jfieldID width; - jfieldID height; - jfieldID refreshRate; - jfieldID density; - jfieldID xDpi; - jfieldID yDpi; - jfieldID secure; -} gPhysicalDisplayInfoClassInfo; - - -class ScreenshotPixelRef : public SkPixelRef { -public: - ScreenshotPixelRef(SkColorTable* ctable) { - fCTable = ctable; - SkSafeRef(ctable); - setImmutable(); - } - - virtual ~ScreenshotPixelRef() { - SkSafeUnref(fCTable); - } - - status_t update(const sp<IBinder>& display, int width, int height, - int minLayer, int maxLayer, bool allLayers) { - status_t res = (width > 0 && height > 0) - ? (allLayers - ? mScreenshot.update(display, width, height) - : mScreenshot.update(display, width, height, minLayer, maxLayer)) - : mScreenshot.update(display); - if (res != NO_ERROR) { - return res; - } - - return NO_ERROR; - } - - uint32_t getWidth() const { - return mScreenshot.getWidth(); - } - - uint32_t getHeight() const { - return mScreenshot.getHeight(); - } - - uint32_t getStride() const { - return mScreenshot.getStride(); - } - - uint32_t getFormat() const { - return mScreenshot.getFormat(); - } - -protected: - // overrides from SkPixelRef - virtual void* onLockPixels(SkColorTable** ct) { - *ct = fCTable; - return (void*)mScreenshot.getPixels(); - } - - virtual void onUnlockPixels() { - } - -private: - ScreenshotClient mScreenshot; - SkColorTable* fCTable; - - typedef SkPixelRef INHERITED; -}; - - // ---------------------------------------------------------------------------- -static sp<SurfaceControl> getSurfaceControl(JNIEnv* env, jobject surfaceObj) { - return reinterpret_cast<SurfaceControl*>( - env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl)); -} - -static void setSurfaceControl(JNIEnv* env, jobject surfaceObj, - const sp<SurfaceControl>& surface) { - SurfaceControl* const p = reinterpret_cast<SurfaceControl*>( - env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl)); - if (surface.get()) { - surface->incStrong(surfaceObj); - } - if (p) { - p->decStrong(surfaceObj); - } - env->SetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl, - reinterpret_cast<jint>(surface.get())); -} - -static sp<Surface> getSurface(JNIEnv* env, jobject surfaceObj) { - sp<Surface> result(android_view_Surface_getSurface(env, surfaceObj)); - if (result == NULL) { - /* - * if this method is called from the WindowManager's process, it means - * the client is is not remote, and therefore is allowed to have - * a Surface (data), so we create it here. - * If we don't have a SurfaceControl, it means we're in a different - * process. - */ - - SurfaceControl* const control = reinterpret_cast<SurfaceControl*>( - env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl)); - if (control) { - result = control->getSurface(); - if (result != NULL) { - result->incStrong(surfaceObj); - env->SetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface, - reinterpret_cast<jint>(result.get())); - } - } - } - return result; -} - -sp<ANativeWindow> android_view_Surface_getNativeWindow(JNIEnv* env, jobject surfaceObj) { - return getSurface(env, surfaceObj); -} - bool android_view_Surface_isInstanceOf(JNIEnv* env, jobject obj) { return env->IsInstanceOf(obj, gSurfaceClassInfo.clazz); } -sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { - return reinterpret_cast<Surface*>( - env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface)); -} - -static void setSurface(JNIEnv* env, jobject surfaceObj, const sp<Surface>& surface) { - Surface* const p = reinterpret_cast<Surface*>( - env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface)); - if (surface.get()) { - surface->incStrong(surfaceObj); - } - if (p) { - p->decStrong(surfaceObj); - } - env->SetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface, - reinterpret_cast<jint>(surface.get())); - - // This test is conservative and it would be better to compare the ISurfaces - if (p && p != surface.get()) { - jint generationId = env->GetIntField(surfaceObj, - gSurfaceClassInfo.mGenerationId); - generationId++; - env->SetIntField(surfaceObj, - gSurfaceClassInfo.mGenerationId, generationId); - } +sp<ANativeWindow> android_view_Surface_getNativeWindow(JNIEnv* env, jobject surfaceObj) { + return android_view_Surface_getSurface(env, surfaceObj); } -static sp<ISurfaceTexture> getISurfaceTexture(JNIEnv* env, jobject surfaceObj) { - if (surfaceObj) { - sp<Surface> surface(getSurface(env, surfaceObj)); - if (surface != NULL) { - return surface->getSurfaceTexture(); - } - } - return NULL; +sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { + return reinterpret_cast<Surface *>( + env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeObject)); } -jobject android_view_Surface_createFromISurfaceTexture(JNIEnv* env, - const sp<ISurfaceTexture>& surfaceTexture) { - if (surfaceTexture == NULL) { +jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, + const sp<IGraphicBufferProducer>& bufferProducer) { + if (bufferProducer == NULL) { return NULL; } - sp<Surface> surface(new Surface(surfaceTexture)); + sp<Surface> surface(new Surface(bufferProducer)); if (surface == NULL) { return NULL; } - jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz, gSurfaceClassInfo.ctor); + jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz, gSurfaceClassInfo.ctor, surface.get()); if (surfaceObj == NULL) { if (env->ExceptionCheck()) { - ALOGE("Could not create instance of Surface from ISurfaceTexture."); + ALOGE("Could not create instance of Surface from IGraphicBufferProducer."); LOGE_EX(env); env->ExceptionClear(); } return NULL; } - - setSurface(env, surfaceObj, surface); + surface->incStrong(surfaceObj); return surfaceObj; } - // ---------------------------------------------------------------------------- -static void nativeCreate(JNIEnv* env, jobject surfaceObj, jobject sessionObj, - jstring nameStr, jint w, jint h, jint format, jint flags) { - ScopedUtfChars name(env, nameStr); - sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); - - sp<SurfaceControl> surface = client->createSurface( - String8(name.c_str()), w, h, format, flags); - if (surface == NULL) { - jniThrowException(env, OutOfResourcesException, NULL); - return; - } - - setSurfaceControl(env, surfaceObj, surface); +static bool isSurfaceValid(const sp<Surface>& sur) { + return sur != 0 && sur->getISurfaceTexture() != 0; } -static void nativeCreateFromSurfaceTexture(JNIEnv* env, jobject surfaceObj, +// ---------------------------------------------------------------------------- + +static jint nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz, jobject surfaceTextureObj) { - sp<SurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj)); + sp<GLConsumer> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj)); if (st == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", "SurfaceTexture has already been released"); - return; + return 0; } - sp<ISurfaceTexture> bq = st->getBufferQueue(); - + sp<IGraphicBufferProducer> bq = st->getBufferQueue(); sp<Surface> surface(new Surface(bq)); if (surface == NULL) { jniThrowException(env, OutOfResourcesException, NULL); - return; + return 0; } - setSurface(env, surfaceObj, surface); + surface->incStrong(clazz); + return int(surface.get()); } -static void nativeRelease(JNIEnv* env, jobject surfaceObj) { - setSurfaceControl(env, surfaceObj, NULL); - setSurface(env, surfaceObj, NULL); +static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) { + sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject)); + sur->decStrong(clazz); } -static void nativeDestroy(JNIEnv* env, jobject surfaceObj) { - sp<SurfaceControl> surfaceControl(getSurfaceControl(env, surfaceObj)); - if (SurfaceControl::isValid(surfaceControl)) { - surfaceControl->clear(); - } - setSurfaceControl(env, surfaceObj, NULL); - setSurface(env, surfaceObj, NULL); +static void nativeDestroy(JNIEnv* env, jclass clazz, jint nativeObject) { + sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject)); + sur->decStrong(clazz); } -static jboolean nativeIsValid(JNIEnv* env, jobject surfaceObj) { - sp<SurfaceControl> surfaceControl(getSurfaceControl(env, surfaceObj)); - if (surfaceControl != NULL) { - return SurfaceControl::isValid(surfaceControl) ? JNI_TRUE : JNI_FALSE; - } - - sp<Surface> surface(getSurface(env, surfaceObj)); - return Surface::isValid(surface) ? JNI_TRUE : JNI_FALSE; -} - -static jint nativeGetIdentity(JNIEnv* env, jobject surfaceObj) { - sp<SurfaceControl> control(getSurfaceControl(env, surfaceObj)); - if (control != NULL) { - return jint(control->getIdentity()); - } - - sp<Surface> surface(getSurface(env, surfaceObj)); - if (surface != NULL) { - return jint(surface->getIdentity()); - } - - return -1; +static jboolean nativeIsValid(JNIEnv* env, jclass clazz, jint nativeObject) { + sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject)); + return isSurfaceValid(sur) ? JNI_TRUE : JNI_FALSE; } -static jboolean nativeIsConsumerRunningBehind(JNIEnv* env, jobject surfaceObj) { - sp<Surface> surface(getSurface(env, surfaceObj)); - if (!Surface::isValid(surface)) { +static jboolean nativeIsConsumerRunningBehind(JNIEnv* env, jclass clazz, jint nativeObject) { + sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject)); + if (!isSurfaceValid(sur)) { doThrowIAE(env); return JNI_FALSE; } - int value = 0; - ANativeWindow* anw = static_cast<ANativeWindow*>(surface.get()); + ANativeWindow* anw = static_cast<ANativeWindow*>(sur.get()); anw->query(anw, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value); return value; } @@ -373,9 +180,10 @@ static inline SkBitmap::Config convertPixelFormat(PixelFormat format) { } } -static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRectObj) { - sp<Surface> surface(getSurface(env, surfaceObj)); - if (!Surface::isValid(surface)) { +static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jint nativeObject, jobject dirtyRectObj) { + sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject)); + + if (!isSurfaceValid(surface)) { doThrowIAE(env); return NULL; } @@ -395,8 +203,10 @@ static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRe dirtyRegion.set(Rect(0x3FFF, 0x3FFF)); } - Surface::SurfaceInfo info; - status_t err = surface->lock(&info, &dirtyRegion); + ANativeWindow_Buffer outBuffer; + Rect dirtyBounds(dirtyRegion.getBounds()); + status_t err = surface->lock(&outBuffer, &dirtyBounds); + dirtyRegion.set(dirtyBounds); if (err < 0) { const char* const exception = (err == NO_MEMORY) ? OutOfResourcesException : @@ -407,18 +217,18 @@ static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRe // Associate a SkCanvas object to this surface jobject canvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas); - env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, info.format); + env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format); SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>( env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas)); SkBitmap bitmap; - ssize_t bpr = info.s * bytesPerPixel(info.format); - bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr); - if (info.format == PIXEL_FORMAT_RGBX_8888) { + ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); + bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr); + if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) { bitmap.setIsOpaque(true); } - if (info.w > 0 && info.h > 0) { - bitmap.setPixels(info.bits); + if (outBuffer.width > 0 && outBuffer.height > 0) { + bitmap.setPixels(outBuffer.bits); } else { // be safe with an empty bitmap. bitmap.setPixels(NULL); @@ -454,15 +264,15 @@ static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRe return canvasObj; } -static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jobject canvasObj) { +static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jint nativeObject, jobject canvasObj) { jobject ownCanvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas); if (!env->IsSameObject(ownCanvasObj, canvasObj)) { doThrowIAE(env); return; } - sp<Surface> surface(getSurface(env, surfaceObj)); - if (!Surface::isValid(surface)) { + sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject)); + if (!isSurfaceValid(surface)) { return; } @@ -481,395 +291,81 @@ static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jobject c } } -static jobject nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, - jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) { - sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); - if (displayToken == NULL) { - return NULL; - } - - ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL); - if (pixels->update(displayToken, width, height, - minLayer, maxLayer, allLayers) != NO_ERROR) { - delete pixels; - return NULL; - } - - uint32_t w = pixels->getWidth(); - uint32_t h = pixels->getHeight(); - uint32_t s = pixels->getStride(); - uint32_t f = pixels->getFormat(); - ssize_t bpr = s * android::bytesPerPixel(f); - - SkBitmap* bitmap = new SkBitmap(); - bitmap->setConfig(convertPixelFormat(f), w, h, bpr); - if (f == PIXEL_FORMAT_RGBX_8888) { - bitmap->setIsOpaque(true); - } - - if (w > 0 && h > 0) { - bitmap->setPixelRef(pixels)->unref(); - bitmap->lockPixels(); - } else { - // be safe with an empty bitmap. - delete pixels; - bitmap->setPixels(NULL); - } - - return GraphicsJNI::createBitmap(env, bitmap, false, NULL); -} - -static void nativeOpenTransaction(JNIEnv* env, jclass clazz) { - SurfaceComposerClient::openGlobalTransaction(); -} - -static void nativeCloseTransaction(JNIEnv* env, jclass clazz) { - SurfaceComposerClient::closeGlobalTransaction(); -} - -static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz) { - SurfaceComposerClient::setAnimationTransaction(); -} - -static void nativeSetLayer(JNIEnv* env, jobject surfaceObj, jint zorder) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setLayer(zorder); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetPosition(JNIEnv* env, jobject surfaceObj, jfloat x, jfloat y) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setPosition(x, y); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetSize(JNIEnv* env, jobject surfaceObj, jint w, jint h) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setSize(w, h); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetFlags(JNIEnv* env, jobject surfaceObj, jint flags, jint mask) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setFlags(flags, mask); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetTransparentRegionHint(JNIEnv* env, jobject surfaceObj, jobject regionObj) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj); - if (!region) { - doThrowIAE(env); - return; - } - - const SkIRect& b(region->getBounds()); - Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom)); - if (region->isComplex()) { - SkRegion::Iterator it(*region); - while (!it.done()) { - const SkIRect& r(it.rect()); - reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom); - it.next(); - } - } - - status_t err = surface->setTransparentRegionHint(reg); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetAlpha(JNIEnv* env, jobject surfaceObj, jfloat alpha) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setAlpha(alpha); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetMatrix(JNIEnv* env, jobject surfaceObj, - jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setMatrix(dsdx, dtdx, dsdy, dtdy); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetWindowCrop(JNIEnv* env, jobject surfaceObj, jobject cropObj) { - const sp<SurfaceControl>& surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - Rect crop; - if (cropObj) { - crop.left = env->GetIntField(cropObj, gRectClassInfo.left); - crop.top = env->GetIntField(cropObj, gRectClassInfo.top); - crop.right = env->GetIntField(cropObj, gRectClassInfo.right); - crop.bottom = env->GetIntField(cropObj, gRectClassInfo.bottom); - } else { - crop.left = crop.top = crop.right = crop.bottom = 0; - } - - status_t err = surface->setCrop(crop); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static void nativeSetLayerStack(JNIEnv* env, jobject surfaceObj, jint layerStack) { - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - if (surface == NULL) return; - - status_t err = surface->setLayerStack(layerStack); - if (err < 0 && err != NO_INIT) { - doThrowIAE(env); - } -} - -static jobject nativeGetBuiltInDisplay(JNIEnv* env, jclass clazz, jint id) { - sp<IBinder> token(SurfaceComposerClient::getBuiltInDisplay(id)); - return javaObjectForIBinder(env, token); -} - -static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj, - jboolean secure) { - ScopedUtfChars name(env, nameObj); - sp<IBinder> token(SurfaceComposerClient::createDisplay( - String8(name.c_str()), bool(secure))); - return javaObjectForIBinder(env, token); -} - -static void nativeSetDisplaySurface(JNIEnv* env, jclass clazz, - jobject tokenObj, jobject surfaceObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return; - - sp<ISurfaceTexture> surfaceTexture(getISurfaceTexture(env, surfaceObj)); - SurfaceComposerClient::setDisplaySurface(token, surfaceTexture); -} - -static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz, - jobject tokenObj, jint layerStack) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return; - - SurfaceComposerClient::setDisplayLayerStack(token, layerStack); -} - -static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz, - jobject tokenObj, jint orientation, jobject layerStackRectObj, jobject displayRectObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return; - - Rect layerStackRect; - layerStackRect.left = env->GetIntField(layerStackRectObj, gRectClassInfo.left); - layerStackRect.top = env->GetIntField(layerStackRectObj, gRectClassInfo.top); - layerStackRect.right = env->GetIntField(layerStackRectObj, gRectClassInfo.right); - layerStackRect.bottom = env->GetIntField(layerStackRectObj, gRectClassInfo.bottom); - - Rect displayRect; - displayRect.left = env->GetIntField(displayRectObj, gRectClassInfo.left); - displayRect.top = env->GetIntField(displayRectObj, gRectClassInfo.top); - displayRect.right = env->GetIntField(displayRectObj, gRectClassInfo.right); - displayRect.bottom = env->GetIntField(displayRectObj, gRectClassInfo.bottom); - - SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect); -} - -static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz, - jobject tokenObj, jobject infoObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return JNI_FALSE; - - DisplayInfo info; - if (SurfaceComposerClient::getDisplayInfo(token, &info)) { - return JNI_FALSE; - } - - env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.width, info.w); - env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.height, info.h); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.refreshRate, info.fps); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.density, info.density); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi); - env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure); - return JNI_TRUE; -} - -static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return; - - ALOGD_IF_SLOW(100, "Excessive delay in blankDisplay() while turning screen off"); - SurfaceComposerClient::blankDisplay(token); -} - -static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return; - - ALOGD_IF_SLOW(100, "Excessive delay in unblankDisplay() while turning screen on"); - SurfaceComposerClient::unblankDisplay(token); -} - // ---------------------------------------------------------------------------- -static void nativeCopyFrom(JNIEnv* env, jobject surfaceObj, jobject otherObj) { +static jint nativeCopyFrom(JNIEnv* env, jclass clazz, + jint nativeObject, jint surfaceControlNativeObj) { /* * This is used by the WindowManagerService just after constructing * a Surface and is necessary for returning the Surface reference to * the caller. At this point, we should only have a SurfaceControl. */ - sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj)); - sp<SurfaceControl> other(getSurfaceControl(env, otherObj)); - if (!SurfaceControl::isSameSurface(surface, other)) { - // we reassign the surface only if it's a different one - // otherwise we would loose our client-side state. - setSurfaceControl(env, surfaceObj, other); + sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj)); + sp<Surface> other(ctrl->getSurface()); + if (other != NULL) { + other->incStrong(clazz); + } + + sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject)); + if (sur != NULL) { + sur->decStrong(clazz); } -} -static void nativeTransferFrom(JNIEnv* env, jobject surfaceObj, jobject otherObj) { - sp<SurfaceControl> control(getSurfaceControl(env, otherObj)); - sp<Surface> surface(android_view_Surface_getSurface(env, otherObj)); - setSurfaceControl(env, surfaceObj, control); - setSurface(env, surfaceObj, surface); - setSurfaceControl(env, otherObj, NULL); - setSurface(env, otherObj, NULL); + return int(other.get()); } -static void nativeReadFromParcel(JNIEnv* env, jobject surfaceObj, jobject parcelObj) { +static jint nativeReadFromParcel(JNIEnv* env, jclass clazz, + jint nativeObject, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { doThrowNPE(env); - return; + return 0; } - - sp<Surface> surface(Surface::readFromParcel(*parcel)); - setSurfaceControl(env, surfaceObj, NULL); - setSurface(env, surfaceObj, surface); + sp<Surface> self(reinterpret_cast<Surface *>(nativeObject)); + if (self != NULL) { + self->decStrong(clazz); + } + sp<Surface> sur(Surface::readFromParcel(*parcel)); + if (sur != NULL) { + sur->incStrong(clazz); + } + return int(sur.get()); } -static void nativeWriteToParcel(JNIEnv* env, jobject surfaceObj, jobject parcelObj) { +static void nativeWriteToParcel(JNIEnv* env, jclass clazz, + jint nativeObject, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { doThrowNPE(env); return; } - - // The Java instance may have a SurfaceControl (in the case of the - // WindowManager or a system app). In that case, we defer to the - // SurfaceControl to send its ISurface. Otherwise, if the Surface is - // available we let it parcel itself. Finally, if the Surface is also - // NULL we fall back to using the SurfaceControl path which sends an - // empty surface; this matches legacy behavior. - sp<SurfaceControl> control(getSurfaceControl(env, surfaceObj)); - if (control != NULL) { - SurfaceControl::writeSurfaceToParcel(control, parcel); - } else { - sp<Surface> surface(android_view_Surface_getSurface(env, surfaceObj)); - if (surface != NULL) { - Surface::writeToParcel(surface, parcel); - } else { - SurfaceControl::writeSurfaceToParcel(NULL, parcel); - } - } + sp<Surface> self(reinterpret_cast<Surface *>(nativeObject)); + Surface::writeToParcel(self, parcel); } // ---------------------------------------------------------------------------- static JNINativeMethod gSurfaceMethods[] = { - {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIII)V", - (void*)nativeCreate }, - {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V", + {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)I", (void*)nativeCreateFromSurfaceTexture }, - {"nativeRelease", "()V", + {"nativeRelease", "(I)V", (void*)nativeRelease }, - {"nativeDestroy", "()V", + {"nativeDestroy", "(I)V", (void*)nativeDestroy }, - {"nativeIsValid", "()Z", + {"nativeIsValid", "(I)Z", (void*)nativeIsValid }, - {"nativeGetIdentity", "()I", - (void*)nativeGetIdentity }, - {"nativeIsConsumerRunningBehind", "()Z", + {"nativeIsConsumerRunningBehind", "(I)Z", (void*)nativeIsConsumerRunningBehind }, - {"nativeLockCanvas", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;", + {"nativeLockCanvas", "(ILandroid/graphics/Rect;)Landroid/graphics/Canvas;", (void*)nativeLockCanvas }, - {"nativeUnlockCanvasAndPost", "(Landroid/graphics/Canvas;)V", + {"nativeUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)V", (void*)nativeUnlockCanvasAndPost }, - {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZ)Landroid/graphics/Bitmap;", - (void*)nativeScreenshot }, - {"nativeOpenTransaction", "()V", - (void*)nativeOpenTransaction }, - {"nativeCloseTransaction", "()V", - (void*)nativeCloseTransaction }, - {"nativeSetAnimationTransaction", "()V", - (void*)nativeSetAnimationTransaction }, - {"nativeSetLayer", "(I)V", - (void*)nativeSetLayer }, - {"nativeSetPosition", "(FF)V", - (void*)nativeSetPosition }, - {"nativeSetSize", "(II)V", - (void*)nativeSetSize }, - {"nativeSetTransparentRegionHint", "(Landroid/graphics/Region;)V", - (void*)nativeSetTransparentRegionHint }, - {"nativeSetAlpha", "(F)V", - (void*)nativeSetAlpha }, - {"nativeSetMatrix", "(FFFF)V", - (void*)nativeSetMatrix }, - {"nativeSetFlags", "(II)V", - (void*)nativeSetFlags }, - {"nativeSetWindowCrop", "(Landroid/graphics/Rect;)V", - (void*)nativeSetWindowCrop }, - {"nativeSetLayerStack", "(I)V", - (void*)nativeSetLayerStack }, - {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;", - (void*)nativeGetBuiltInDisplay }, - {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;", - (void*)nativeCreateDisplay }, - {"nativeSetDisplaySurface", "(Landroid/os/IBinder;Landroid/view/Surface;)V", - (void*)nativeSetDisplaySurface }, - {"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V", - (void*)nativeSetDisplayLayerStack }, - {"nativeSetDisplayProjection", "(Landroid/os/IBinder;ILandroid/graphics/Rect;Landroid/graphics/Rect;)V", - (void*)nativeSetDisplayProjection }, - {"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/Surface$PhysicalDisplayInfo;)Z", - (void*)nativeGetDisplayInfo }, - {"nativeBlankDisplay", "(Landroid/os/IBinder;)V", - (void*)nativeBlankDisplay }, - {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V", - (void*)nativeUnblankDisplay }, - {"nativeCopyFrom", "(Landroid/view/Surface;)V", + {"nativeCopyFrom", "(II)I", (void*)nativeCopyFrom }, - {"nativeTransferFrom", "(Landroid/view/Surface;)V", - (void*)nativeTransferFrom }, - {"nativeReadFromParcel", "(Landroid/os/Parcel;)V", + {"nativeReadFromParcel", "(ILandroid/os/Parcel;)I", (void*)nativeReadFromParcel }, - {"nativeWriteToParcel", "(Landroid/os/Parcel;)V", + {"nativeWriteToParcel", "(ILandroid/os/Parcel;)V", (void*)nativeWriteToParcel }, }; @@ -880,17 +376,15 @@ int register_android_view_Surface(JNIEnv* env) jclass clazz = env->FindClass("android/view/Surface"); gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz)); - gSurfaceClassInfo.mNativeSurface = - env->GetFieldID(gSurfaceClassInfo.clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I"); - gSurfaceClassInfo.mNativeSurfaceControl = - env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeSurfaceControl", "I"); + gSurfaceClassInfo.mNativeObject = + env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject", "I"); gSurfaceClassInfo.mGenerationId = env->GetFieldID(gSurfaceClassInfo.clazz, "mGenerationId", "I"); gSurfaceClassInfo.mCanvas = env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvas", "Landroid/graphics/Canvas;"); gSurfaceClassInfo.mCanvasSaveCount = env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvasSaveCount", "I"); - gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "()V"); + gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "(I)V"); clazz = env->FindClass("android/graphics/Canvas"); gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I"); @@ -902,14 +396,6 @@ int register_android_view_Surface(JNIEnv* env) gRectClassInfo.right = env->GetFieldID(clazz, "right", "I"); gRectClassInfo.bottom = env->GetFieldID(clazz, "bottom", "I"); - clazz = env->FindClass("android/view/Surface$PhysicalDisplayInfo"); - gPhysicalDisplayInfoClassInfo.width = env->GetFieldID(clazz, "width", "I"); - gPhysicalDisplayInfoClassInfo.height = env->GetFieldID(clazz, "height", "I"); - gPhysicalDisplayInfoClassInfo.refreshRate = env->GetFieldID(clazz, "refreshRate", "F"); - gPhysicalDisplayInfoClassInfo.density = env->GetFieldID(clazz, "density", "F"); - gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F"); - gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F"); - gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z"); return err; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp new file mode 100644 index 0000000..e477e54 --- /dev/null +++ b/core/jni/android_view_SurfaceControl.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceControl" + +#include <stdio.h> + +#include "jni.h" +#include "JNIHelp.h" + +#include "android_os_Parcel.h" +#include "android_util_Binder.h" +#include "android/graphics/GraphicsJNI.h" +#include "android/graphics/Region.h" + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_view_SurfaceSession.h> + +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <ui/DisplayInfo.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +#include <utils/Log.h> + +#include <ScopedUtfChars.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +static const char* const OutOfResourcesException = + "android/view/Surface$OutOfResourcesException"; + +static struct { + jfieldID width; + jfieldID height; + jfieldID refreshRate; + jfieldID density; + jfieldID xDpi; + jfieldID yDpi; + jfieldID secure; +} gPhysicalDisplayInfoClassInfo; + + +class ScreenshotPixelRef : public SkPixelRef { +public: + ScreenshotPixelRef(SkColorTable* ctable) { + fCTable = ctable; + SkSafeRef(ctable); + setImmutable(); + } + + virtual ~ScreenshotPixelRef() { + SkSafeUnref(fCTable); + } + + status_t update(const sp<IBinder>& display, int width, int height, + int minLayer, int maxLayer, bool allLayers) { + status_t res = (width > 0 && height > 0) + ? (allLayers + ? mScreenshot.update(display, width, height) + : mScreenshot.update(display, width, height, minLayer, maxLayer)) + : mScreenshot.update(display); + if (res != NO_ERROR) { + return res; + } + + return NO_ERROR; + } + + uint32_t getWidth() const { + return mScreenshot.getWidth(); + } + + uint32_t getHeight() const { + return mScreenshot.getHeight(); + } + + uint32_t getStride() const { + return mScreenshot.getStride(); + } + + uint32_t getFormat() const { + return mScreenshot.getFormat(); + } + +protected: + // overrides from SkPixelRef + virtual void* onLockPixels(SkColorTable** ct) { + *ct = fCTable; + return (void*)mScreenshot.getPixels(); + } + + virtual void onUnlockPixels() { + } + +private: + ScreenshotClient mScreenshot; + SkColorTable* fCTable; + + typedef SkPixelRef INHERITED; +}; + + +// ---------------------------------------------------------------------------- + +static jint nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, + jstring nameStr, jint w, jint h, jint format, jint flags) { + ScopedUtfChars name(env, nameStr); + sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); + sp<SurfaceControl> surface = client->createSurface( + String8(name.c_str()), w, h, format, flags); + if (surface == NULL) { + jniThrowException(env, OutOfResourcesException, NULL); + return 0; + } + surface->incStrong(clazz); + return int(surface.get()); +} + +static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) { + sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject)); + ctrl->decStrong(clazz); +} + +static void nativeDestroy(JNIEnv* env, jclass clazz, jint nativeObject) { + sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject)); + ctrl->clear(); + ctrl->decStrong(clazz); +} + +static inline SkBitmap::Config convertPixelFormat(PixelFormat format) { + /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then + we can map to SkBitmap::kARGB_8888_Config, and optionally call + bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator) + */ + switch (format) { + case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config; + case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config; + case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config; + case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config; + case PIXEL_FORMAT_A_8: return SkBitmap::kA8_Config; + default: return SkBitmap::kNo_Config; + } +} + +static jobject nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, + jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) { + sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); + if (displayToken == NULL) { + return NULL; + } + + ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL); + if (pixels->update(displayToken, width, height, + minLayer, maxLayer, allLayers) != NO_ERROR) { + delete pixels; + return NULL; + } + + uint32_t w = pixels->getWidth(); + uint32_t h = pixels->getHeight(); + uint32_t s = pixels->getStride(); + uint32_t f = pixels->getFormat(); + ssize_t bpr = s * android::bytesPerPixel(f); + + SkBitmap* bitmap = new SkBitmap(); + bitmap->setConfig(convertPixelFormat(f), w, h, bpr); + if (f == PIXEL_FORMAT_RGBX_8888) { + bitmap->setIsOpaque(true); + } + + if (w > 0 && h > 0) { + bitmap->setPixelRef(pixels)->unref(); + bitmap->lockPixels(); + } else { + // be safe with an empty bitmap. + delete pixels; + bitmap->setPixels(NULL); + } + + return GraphicsJNI::createBitmap(env, bitmap, false, NULL); +} + +static void nativeOpenTransaction(JNIEnv* env, jclass clazz) { + SurfaceComposerClient::openGlobalTransaction(); +} + +static void nativeCloseTransaction(JNIEnv* env, jclass clazz) { + SurfaceComposerClient::closeGlobalTransaction(); +} + +static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz) { + SurfaceComposerClient::setAnimationTransaction(); +} + +static void nativeSetLayer(JNIEnv* env, jclass clazz, jint nativeObject, jint zorder) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setLayer(zorder); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetPosition(JNIEnv* env, jclass clazz, jint nativeObject, jfloat x, jfloat y) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setPosition(x, y); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetSize(JNIEnv* env, jclass clazz, jint nativeObject, jint w, jint h) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setSize(w, h); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetFlags(JNIEnv* env, jclass clazz, jint nativeObject, jint flags, jint mask) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setFlags(flags, mask); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jint nativeObject, jobject regionObj) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj); + if (!region) { + doThrowIAE(env); + return; + } + + const SkIRect& b(region->getBounds()); + Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom)); + if (region->isComplex()) { + SkRegion::Iterator it(*region); + while (!it.done()) { + const SkIRect& r(it.rect()); + reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom); + it.next(); + } + } + + status_t err = ctrl->setTransparentRegionHint(reg); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetAlpha(JNIEnv* env, jclass clazz, jint nativeObject, jfloat alpha) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setAlpha(alpha); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetMatrix(JNIEnv* env, jclass clazz, jint nativeObject, + jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setMatrix(dsdx, dtdx, dsdy, dtdy); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jint nativeObject, + jint l, jint t, jint r, jint b) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + Rect crop(l, t, r, b); + status_t err = ctrl->setCrop(crop); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jint nativeObject, jint layerStack) { + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + status_t err = ctrl->setLayerStack(layerStack); + if (err < 0 && err != NO_INIT) { + doThrowIAE(env); + } +} + +static jobject nativeGetBuiltInDisplay(JNIEnv* env, jclass clazz, jint id) { + sp<IBinder> token(SurfaceComposerClient::getBuiltInDisplay(id)); + return javaObjectForIBinder(env, token); +} + +static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj, + jboolean secure) { + ScopedUtfChars name(env, nameObj); + sp<IBinder> token(SurfaceComposerClient::createDisplay( + String8(name.c_str()), bool(secure))); + return javaObjectForIBinder(env, token); +} + +static void nativeSetDisplaySurface(JNIEnv* env, jclass clazz, + jobject tokenObj, jint nativeSurfaceObject) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + sp<Surface> sur(reinterpret_cast<Surface *>(nativeSurfaceObject)); + sp<IGraphicBufferProducer> bufferProducer(sur->getIGraphicBufferProducer()); + SurfaceComposerClient::setDisplaySurface(token, bufferProducer); +} + +static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz, + jobject tokenObj, jint layerStack) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + + SurfaceComposerClient::setDisplayLayerStack(token, layerStack); +} + +static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz, + jobject tokenObj, jint orientation, + jint layerStackRect_left, jint layerStackRect_top, jint layerStackRect_right, jint layerStackRect_bottom, + jint displayRect_left, jint displayRect_top, jint displayRect_right, jint displayRect_bottom) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + Rect layerStackRect(layerStackRect_left, layerStackRect_top, layerStackRect_right, layerStackRect_bottom); + Rect displayRect(displayRect_left, displayRect_top, displayRect_right, displayRect_bottom); + SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect); +} + +static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz, + jobject tokenObj, jobject infoObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return JNI_FALSE; + + DisplayInfo info; + if (SurfaceComposerClient::getDisplayInfo(token, &info)) { + return JNI_FALSE; + } + + env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.width, info.w); + env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.height, info.h); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.refreshRate, info.fps); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.density, info.density); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi); + env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure); + return JNI_TRUE; +} + +static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + + ALOGD_IF_SLOW(100, "Excessive delay in blankDisplay() while turning screen off"); + SurfaceComposerClient::blankDisplay(token); +} + +static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + + ALOGD_IF_SLOW(100, "Excessive delay in unblankDisplay() while turning screen on"); + SurfaceComposerClient::unblankDisplay(token); +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod sSurfaceControlMethods[] = { + {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIII)I", + (void*)nativeCreate }, + {"nativeRelease", "(I)V", + (void*)nativeRelease }, + {"nativeDestroy", "(I)V", + (void*)nativeDestroy }, + {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZ)Landroid/graphics/Bitmap;", + (void*)nativeScreenshot }, + {"nativeOpenTransaction", "()V", + (void*)nativeOpenTransaction }, + {"nativeCloseTransaction", "()V", + (void*)nativeCloseTransaction }, + {"nativeSetAnimationTransaction", "()V", + (void*)nativeSetAnimationTransaction }, + {"nativeSetLayer", "(II)V", + (void*)nativeSetLayer }, + {"nativeSetPosition", "(IFF)V", + (void*)nativeSetPosition }, + {"nativeSetSize", "(III)V", + (void*)nativeSetSize }, + {"nativeSetTransparentRegionHint", "(ILandroid/graphics/Region;)V", + (void*)nativeSetTransparentRegionHint }, + {"nativeSetAlpha", "(IF)V", + (void*)nativeSetAlpha }, + {"nativeSetMatrix", "(IFFFF)V", + (void*)nativeSetMatrix }, + {"nativeSetFlags", "(III)V", + (void*)nativeSetFlags }, + {"nativeSetWindowCrop", "(IIIII)V", + (void*)nativeSetWindowCrop }, + {"nativeSetLayerStack", "(II)V", + (void*)nativeSetLayerStack }, + {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;", + (void*)nativeGetBuiltInDisplay }, + {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;", + (void*)nativeCreateDisplay }, + {"nativeSetDisplaySurface", "(Landroid/os/IBinder;I)V", + (void*)nativeSetDisplaySurface }, + {"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V", + (void*)nativeSetDisplayLayerStack }, + {"nativeSetDisplayProjection", "(Landroid/os/IBinder;IIIIIIIII)V", + (void*)nativeSetDisplayProjection }, + {"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/SurfaceControl$PhysicalDisplayInfo;)Z", + (void*)nativeGetDisplayInfo }, + {"nativeBlankDisplay", "(Landroid/os/IBinder;)V", + (void*)nativeBlankDisplay }, + {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V", + (void*)nativeUnblankDisplay }, +}; + +int register_android_view_SurfaceControl(JNIEnv* env) +{ + int err = AndroidRuntime::registerNativeMethods(env, "android/view/SurfaceControl", + sSurfaceControlMethods, NELEM(sSurfaceControlMethods)); + + jclass clazz = env->FindClass("android/view/SurfaceControl$PhysicalDisplayInfo"); + gPhysicalDisplayInfoClassInfo.width = env->GetFieldID(clazz, "width", "I"); + gPhysicalDisplayInfoClassInfo.height = env->GetFieldID(clazz, "height", "I"); + gPhysicalDisplayInfoClassInfo.refreshRate = env->GetFieldID(clazz, "refreshRate", "F"); + gPhysicalDisplayInfoClassInfo.density = env->GetFieldID(clazz, "density", "F"); + gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F"); + gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F"); + gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z"); + return err; +} + +}; diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index 9484c6b..e75a2d8 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -22,8 +22,8 @@ #include <ui/Region.h> #include <ui/Rect.h> -#include <gui/SurfaceTexture.h> -#include <gui/SurfaceTextureClient.h> +#include <gui/GLConsumer.h> +#include <gui/Surface.h> #include <SkBitmap.h> #include <SkCanvas.h> @@ -67,8 +67,8 @@ static struct { static void android_view_TextureView_setDefaultBufferSize(JNIEnv* env, jobject, jobject surface, jint width, jint height) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); - surfaceTexture->setDefaultBufferSize(width, height); + sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(env, surface)); + glConsumer->setDefaultBufferSize(width, height); } static inline SkBitmap::Config convertPixelFormat(int32_t format) { @@ -101,8 +101,8 @@ static int32_t native_window_unlockAndPost(ANativeWindow* window) { static void android_view_TextureView_createNativeWindow(JNIEnv* env, jobject textureView, jobject surface) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); - sp<ANativeWindow> window = new SurfaceTextureClient(surfaceTexture); + sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(env, surface)); + sp<ANativeWindow> window = new Surface(glConsumer->getBufferQueue()); window->incStrong(0); SET_INT(textureView, gTextureViewClassInfo.nativeWindow, jint(window.get())); diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp new file mode 100644 index 0000000..0906593 --- /dev/null +++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "NetworkStats" + +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <android_runtime/AndroidRuntime.h> +#include <cutils/logger.h> +#include <jni.h> + +#include <ScopedUtfChars.h> +#include <ScopedLocalRef.h> +#include <ScopedPrimitiveArray.h> + +#include <utils/Log.h> +#include <utils/misc.h> +#include <utils/Vector.h> + +namespace android { + +static jclass gStringClass; + +static struct { + jfieldID size; + jfieldID iface; + jfieldID uid; + jfieldID set; + jfieldID tag; + jfieldID rxBytes; + jfieldID rxPackets; + jfieldID txBytes; + jfieldID txPackets; + jfieldID operations; +} gNetworkStatsClassInfo; + +struct stats_line { + int32_t idx; + char iface[32]; + int32_t uid; + int32_t set; + int32_t tag; + int64_t rxBytes; + int64_t rxPackets; + int64_t txBytes; + int64_t txPackets; +}; + +static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, + jstring path, jint limitUid) { + ScopedUtfChars path8(env, path); + if (path8.c_str() == NULL) { + return -1; + } + + FILE *fp = fopen(path8.c_str(), "r"); + if (fp == NULL) { + return -1; + } + + Vector<stats_line> lines; + + int lastIdx = 1; + char buffer[384]; + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + stats_line s; + int64_t rawTag; + if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &s.idx, + &s.iface, &rawTag, &s.uid, &s.set, &s.rxBytes, &s.rxPackets, + &s.txBytes, &s.txPackets) == 9) { + if (s.idx != lastIdx + 1) { + ALOGE("inconsistent idx=%d after lastIdx=%d", s.idx, lastIdx); + return -1; + } + lastIdx = s.idx; + + s.tag = rawTag >> 32; + lines.push_back(s); + } + } + + if (fclose(fp) != 0) { + return -1; + } + + int size = lines.size(); + + ScopedLocalRef<jobjectArray> iface(env, env->NewObjectArray(size, gStringClass, NULL)); + if (iface.get() == NULL) return -1; + ScopedIntArrayRW uid(env, env->NewIntArray(size)); + if (uid.get() == NULL) return -1; + ScopedIntArrayRW set(env, env->NewIntArray(size)); + if (set.get() == NULL) return -1; + ScopedIntArrayRW tag(env, env->NewIntArray(size)); + if (tag.get() == NULL) return -1; + ScopedLongArrayRW rxBytes(env, env->NewLongArray(size)); + if (rxBytes.get() == NULL) return -1; + ScopedLongArrayRW rxPackets(env, env->NewLongArray(size)); + if (rxPackets.get() == NULL) return -1; + ScopedLongArrayRW txBytes(env, env->NewLongArray(size)); + if (txBytes.get() == NULL) return -1; + ScopedLongArrayRW txPackets(env, env->NewLongArray(size)); + if (txPackets.get() == NULL) return -1; + ScopedLongArrayRW operations(env, env->NewLongArray(size)); + if (operations.get() == NULL) return -1; + + for (int i = 0; i < size; i++) { + ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface)); + env->SetObjectArrayElement(iface.get(), i, ifaceString.get()); + + uid[i] = lines[i].uid; + set[i] = lines[i].set; + tag[i] = lines[i].tag; + rxBytes[i] = lines[i].rxBytes; + rxPackets[i] = lines[i].rxPackets; + txBytes[i] = lines[i].txBytes; + txPackets[i] = lines[i].txPackets; + } + + env->SetIntField(stats, gNetworkStatsClassInfo.size, size); + env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get()); + env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray()); + + return 0; +} + +static jclass findClass(JNIEnv* env, const char* name) { + ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); + jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); + if (result == NULL) { + ALOGE("failed to find class '%s'", name); + abort(); + } + return result; +} + +static JNINativeMethod gMethods[] = { + { "nativeReadNetworkStatsDetail", + "(Landroid/net/NetworkStats;Ljava/lang/String;I)I", + (void*) readNetworkStatsDetail } +}; + +int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) { + int err = AndroidRuntime::registerNativeMethods(env, + "com/android/internal/net/NetworkStatsFactory", gMethods, + NELEM(gMethods)); + + gStringClass = findClass(env, "java/lang/String"); + + jclass clazz = env->FindClass("android/net/NetworkStats"); + gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I"); + gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;"); + gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I"); + gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I"); + gNetworkStatsClassInfo.tag = env->GetFieldID(clazz, "tag", "[I"); + gNetworkStatsClassInfo.rxBytes = env->GetFieldID(clazz, "rxBytes", "[J"); + gNetworkStatsClassInfo.rxPackets = env->GetFieldID(clazz, "rxPackets", "[J"); + gNetworkStatsClassInfo.txBytes = env->GetFieldID(clazz, "txBytes", "[J"); + gNetworkStatsClassInfo.txPackets = env->GetFieldID(clazz, "txPackets", "[J"); + gNetworkStatsClassInfo.operations = env->GetFieldID(clazz, "operations", "[J"); + + return err; +} + +} diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp index 7e5dede..bc8c4a7 100644 --- a/core/jni/com_android_internal_os_ZygoteInit.cpp +++ b/core/jni/com_android_internal_os_ZygoteInit.cpp @@ -163,32 +163,6 @@ static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env, } } -static void com_android_internal_os_ZygoteInit_setCapabilities (JNIEnv *env, - jobject clazz, jlong permitted, jlong effective) -{ - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - int err; - - memset (&capheader, 0, sizeof(capheader)); - memset (&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - // As of this writing, capdata is __u32, but that's expected - // to change... - capdata.effective = effective; - capdata.permitted = permitted; - - err = capset (&capheader, &capdata); - - if (err < 0) { - jniThrowIOException(env, errno); - return; - } -} - static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env, jobject clazz, jint pid) { @@ -304,8 +278,6 @@ static JNINativeMethod gMethods[] = { (void *) com_android_internal_os_ZygoteInit_reopenStdio}, { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V", (void *) com_android_internal_os_ZygoteInit_setCloseOnExec}, - { "setCapabilities", "(JJ)V", - (void *) com_android_internal_os_ZygoteInit_setCapabilities }, { "capgetPermitted", "(I)J", (void *) com_android_internal_os_ZygoteInit_capgetPermitted }, { "selectReadable", "([Ljava/io/FileDescriptor;)I", diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index f8904bd..37330ec 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -27,8 +27,8 @@ #include <GLES/gl.h> #include <gui/Surface.h> -#include <gui/SurfaceTexture.h> -#include <gui/SurfaceTextureClient.h> +#include <gui/GLConsumer.h> +#include <gui/Surface.h> #include <SkBitmap.h> #include <SkPixelRef.h> @@ -353,9 +353,9 @@ not_valid_surface: return 0; } - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(_env, native_window)); + sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(_env, native_window)); - window = new SurfaceTextureClient(surfaceTexture); + window = new Surface(glConsumer->getBufferQueue()); if (window == NULL) goto not_valid_surface; diff --git a/core/res/Android.mk b/core/res/Android.mk index 22f4fdc..cfc791d 100644 --- a/core/res/Android.mk +++ b/core/res/Android.mk @@ -42,7 +42,3 @@ include $(BUILD_PACKAGE) # Make sure the system .rs files get compiled before building the package-export.apk. # $(resource_export_package): $(framework_RenderScript_STAMP_FILE) - -# define a global intermediate target that other module may depend on. -.PHONY: framework-res-package-target -framework-res-package-target: $(LOCAL_BUILT_MODULE) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bee9a8c..8a53cc3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -129,6 +129,7 @@ <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" /> <protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" /> + <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" /> <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" /> <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" /> @@ -164,34 +165,13 @@ <protected-broadcast android:name="android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" /> <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" /> <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" /> + <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" /> + <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" /> + <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" /> + <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" /> - - - - <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" /> - - <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" /> - <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" /> - <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" /> - <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" /> - <protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" /> - <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" /> - <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" /> - <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" /> - <protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.p2p.PEERS_CHANGED" /> - <protected-broadcast android:name="android.net.wifi.p2p.CONNECTION_STATE_CHANGE" /> - <protected-broadcast android:name="android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" /> - <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" /> - <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" /> - - - + <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" /> + <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" /> <!-- ====================================== --> <!-- Permissions for things that cost money --> @@ -220,7 +200,7 @@ android:permissionGroupFlags="personalInfo" android:priority="360"/> - <!-- Allows an application to send SMS messages. --> + <!-- Allows an application to send SMS messages. --> <permission android:name="android.permission.SEND_SMS" android:permissionGroup="android.permission-group.MESSAGES" android:protectionLevel="dangerous" @@ -228,14 +208,13 @@ android:label="@string/permlab_sendSms" android:description="@string/permdesc_sendSms" /> - <!-- Allows an application to send SMS messages via the Messaging app with no user - input or confirmation. - @hide --> - <permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION" + <!-- Allows an application (Phone) to send a request to other applications + to handle the respond-via-message action during incoming calls. --> + <permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE" android:permissionGroup="android.permission-group.MESSAGES" android:protectionLevel="signature|system" - android:label="@string/permlab_sendSmsNoConfirmation" - android:description="@string/permdesc_sendSmsNoConfirmation" /> + android:label="@string/permlab_sendRespondViaMessageRequest" + android:description="@string/permdesc_sendRespondViaMessageRequest" /> <!-- Allows an application to monitor incoming SMS messages, to record or perform processing on them. --> @@ -556,6 +535,31 @@ android:label="@string/permlab_addVoicemail" android:description="@string/permdesc_addVoicemail" /> + <!-- =============================================== --> + <!-- Permissions for enabling accessibility features --> + <!-- =============================================== --> + + <!-- Used for permissions that allow requesting certain accessibility features. --> + <permission-group android:name="android.permission-group.ACCESSIBILITY_FEATURES" + android:label="@string/permgrouplab_accessibilityFeatures" + android:icon="@drawable/perm_group_accessibility_features" + android:description="@string/permgroupdesc_accessibilityFeatures" + android:priority="380" /> + + <!-- Allows an accessibility service to request touch exploration mode. --> + <permission android:name="android.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE" + android:permissionGroup="android.permission-group.ACCESSIBILITY_FEATURES" + android:label="@string/permlab_canRequestTouchExplorationMode" + android:description="@string/permdesc_canRequestTouchExplorationMode" + android:protectionLevel="dangerous" /> + + <!-- Allows an accessibility service to request enhanced web accessibility. --> + <permission android:name="android.permission.CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY" + android:permissionGroup="android.permission-group.ACCESSIBILITY_FEATURES" + android:label="@string/permlab_canRequestEnahncedWebAccessibility" + android:description="@string/permdesc_canRequestEnahncedWebAccessibility" + android:protectionLevel="dangerous" /> + <!-- ======================================= --> <!-- Permissions for accessing location info --> <!-- ======================================= --> @@ -641,7 +645,7 @@ android:protectionLevel="normal" android:description="@string/permdesc_accessWifiState" android:label="@string/permlab_accessWifiState" /> - + <!-- Allows applications to change Wi-Fi connectivity state --> <permission android:name="android.permission.CHANGE_WIFI_STATE" android:permissionGroup="android.permission-group.NETWORK" @@ -681,14 +685,14 @@ android:protectionLevel="dangerous" android:description="@string/permdesc_bluetooth" android:label="@string/permlab_bluetooth" /> - + <!-- Allows applications to discover and pair bluetooth devices --> <permission android:name="android.permission.BLUETOOTH_ADMIN" android:permissionGroup="android.permission-group.BLUETOOTH_NETWORK" android:protectionLevel="dangerous" android:description="@string/permdesc_bluetoothAdmin" android:label="@string/permlab_bluetoothAdmin" /> - + <!-- Allows bluetooth stack to access files @hide This should only be used by Bluetooth apk. --> @@ -719,7 +723,7 @@ <permission android:name="android.permission.LOOP_RADIO" android:permissionGroup="android.permission-group.NETWORK" android:protectionLevel="signature|system" /> - + <!-- ================================== --> <!-- Permissions for accessing accounts --> <!-- ================================== --> @@ -1129,7 +1133,7 @@ android:protectionLevel="signature|system" android:label="@string/permlab_manageUsers" android:description="@string/permdesc_manageUsers" /> - + <!-- Allows an application to get full detailed information about recently running tasks, with full fidelity to the real state. @hide --> @@ -1357,21 +1361,6 @@ android:label="@string/permlab_writeGservices" android:description="@string/permdesc_writeGservices" /> - <!-- @hide Change the screen compatibility mode of applications --> - <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" - android:permissionGroup="android.permission-group.SYSTEM_TOOLS" - android:protectionLevel="signature" - android:label="@string/permlab_setScreenCompatibility" - android:description="@string/permdesc_setScreenCompatibility" /> - - <!-- Allows an application to modify the current configuration, such - as locale. --> - <permission android:name="android.permission.CHANGE_CONFIGURATION" - android:permissionGroup="android.permission-group.SYSTEM_TOOLS" - android:protectionLevel="system|signature" - android:label="@string/permlab_changeConfiguration" - android:description="@string/permdesc_changeConfiguration" /> - <!-- Allows an application to call {@link android.app.ActivityManager#forceStopPackage}. @hide --> @@ -1651,6 +1640,20 @@ android:description="@string/permdesc_updateBatteryStats" android:protectionLevel="signature|system" /> + <!-- @hide Allows an application to collect battery statistics --> + <permission android:name="android.permission.GET_APP_OPS_STATS" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:label="@string/permlab_getAppOpsStats" + android:description="@string/permdesc_getAppOpsStats" + android:protectionLevel="signature|system|development" /> + + <!-- Allows an application to update application operation statistics. Not for + use by third party apps. @hide --> + <permission android:name="android.permission.UPDATE_APP_OPS_STATS" + android:label="@string/permlab_updateAppOpsStats" + android:description="@string/permdesc_updateAppOpsStats" + android:protectionLevel="signature|system" /> + <!-- Allows an application to open windows that are for use by parts of the system user interface. Not for use by third party apps. --> <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" @@ -1672,7 +1675,7 @@ android:label="@string/permlab_freezeScreen" android:description="@string/permdesc_freezeScreen" android:protectionLevel="signature" /> - + <!-- Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window. Without this permission, you can only deliver events to windows in your own process. @@ -1733,6 +1736,13 @@ android:description="@string/permdesc_stopAppSwitches" android:protectionLevel="signature|system" /> + <!-- Allows an application to retrieve private information about + the current top activity, such as any assist context it can provide. --> + <permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" + android:label="@string/permlab_getTopActivityInfo" + android:description="@string/permdesc_getTopActivityInfo" + android:protectionLevel="signature" /> + <!-- Allows an application to retrieve the current state of keys and switches. This is only for use by the system. @deprecated The API that used this permission has been removed. --> @@ -1748,7 +1758,7 @@ android:description="@string/permdesc_bindInputMethod" android:protectionLevel="signature" /> - <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService}, + <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService}, to ensure that only the system can bind to it. --> <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" android:label="@string/permlab_bindAccessibilityService" @@ -2169,6 +2179,14 @@ android:description="@string/permdesc_updateLock" android:protectionLevel="signatureOrSystem" /> + <!-- Allows an application to read the current set of notifications, including + any metadata and intents attached. + @hide --> + <permission android:name="android.permission.ACCESS_NOTIFICATIONS" + android:label="@string/permlab_accessNotifications" + android:description="@string/permdesc_accessNotifications" + android:protectionLevel="signature|system" /> + <!-- The system process is explicitly the only one allowed to launch the confirmation UI for full backup/restore --> <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/> @@ -2283,6 +2301,12 @@ </intent-filter> </receiver> + <receiver android:name="com.android.server.updates.TZInfoInstallReceiver" > + <intent-filter> + <action android:name="android.intent.action.UPDATE_TZINFO" /> + </intent-filter> + </receiver> + <receiver android:name="com.android.server.MasterClearReceiver" android:permission="android.permission.MASTER_CLEAR" android:priority="100" > diff --git a/core/res/res/drawable-hdpi/perm_group_accessibility_features.png b/core/res/res/drawable-hdpi/perm_group_accessibility_features.png Binary files differnew file mode 100755 index 0000000..849c19c --- /dev/null +++ b/core/res/res/drawable-hdpi/perm_group_accessibility_features.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification.png Binary files differnew file mode 100644 index 0000000..62ef692 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification_mute.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification_mute.png Binary files differnew file mode 100644 index 0000000..40123e3 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification_mute.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_phone.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_phone.png Binary files differnew file mode 100644 index 0000000..968f5ee --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_phone.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif.png Binary files differnew file mode 100644 index 0000000..e5f0dcf --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_mute.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_mute.png Binary files differnew file mode 100644 index 0000000..371e32f --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_mute.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_vibrate.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_vibrate.png Binary files differnew file mode 100644 index 0000000..e05e8f5 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_vibrate.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol.png Binary files differnew file mode 100644 index 0000000..81a8422 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol_mute.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol_mute.png Binary files differnew file mode 100644 index 0000000..371e32f --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol_mute.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png Binary files differnew file mode 100644 index 0000000..7b1eee6 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png Binary files differnew file mode 100644 index 0000000..aedf9b1 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..57141d5 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..8e28906 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..efdab73 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..307b86d --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..2e28431 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..7a83451 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..9afc912 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png Binary files differnew file mode 100644 index 0000000..7d03855 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..21f1e2c --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..600b861 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..c11619c --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..91be10f --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..c921487 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..4da6c6e --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..9c7d294 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png Binary files differnew file mode 100644 index 0000000..c9f493a --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png Binary files differnew file mode 100644 index 0000000..a7dc29d --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png diff --git a/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png Binary files differnew file mode 100644 index 0000000..c093420 --- /dev/null +++ b/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png diff --git a/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png Binary files differnew file mode 100644 index 0000000..1f21884 --- /dev/null +++ b/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png diff --git a/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png Binary files differnew file mode 100644 index 0000000..d726b7a --- /dev/null +++ b/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification.png Binary files differnew file mode 100644 index 0000000..d9843e0 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification_mute.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification_mute.png Binary files differnew file mode 100644 index 0000000..2159cab --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification_mute.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_phone.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_phone.png Binary files differnew file mode 100644 index 0000000..b5af351 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_phone.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif.png Binary files differnew file mode 100644 index 0000000..6341be6 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_mute.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_mute.png Binary files differnew file mode 100644 index 0000000..b4c3a54 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_mute.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_vibrate.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_vibrate.png Binary files differnew file mode 100644 index 0000000..835773e --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_vibrate.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol.png Binary files differnew file mode 100644 index 0000000..947d1f9 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol_mute.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol_mute.png Binary files differnew file mode 100644 index 0000000..b4c3a54 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol_mute.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png Binary files differnew file mode 100644 index 0000000..cba5500 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png Binary files differnew file mode 100644 index 0000000..0335dec --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..02c799e --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..286157c --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..030f723 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..a5b10d2 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..10faec1 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..62a70ed --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..633b8d2 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png Binary files differnew file mode 100644 index 0000000..59ff556 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..09d5aa4 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..c320ea0 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..a5f28fd --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..9e0d39c --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..24d928f --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..596ba2d --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..55b1af7 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png Binary files differnew file mode 100644 index 0000000..bb0486a --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png Binary files differnew file mode 100644 index 0000000..265c421 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification.png Binary files differnew file mode 100644 index 0000000..43aedea --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification_mute.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification_mute.png Binary files differnew file mode 100644 index 0000000..4e87f77 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification_mute.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_phone.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_phone.png Binary files differnew file mode 100644 index 0000000..1066d03 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_phone.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif.png Binary files differnew file mode 100644 index 0000000..daf9213 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_mute.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_mute.png Binary files differnew file mode 100644 index 0000000..83d3bdd --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_mute.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_vibrate.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_vibrate.png Binary files differnew file mode 100644 index 0000000..4de95aa --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_vibrate.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol.png Binary files differnew file mode 100644 index 0000000..8132926 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol_mute.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol_mute.png Binary files differnew file mode 100644 index 0000000..83d3bdd --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol_mute.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png Binary files differnew file mode 100644 index 0000000..6820a23 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png Binary files differnew file mode 100644 index 0000000..c8690bd --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..6f41b24 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..5a96fc1 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..96a6da5 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..849d795 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..3d6d0f9 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..1cbaf6c --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..878e90d --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png Binary files differnew file mode 100644 index 0000000..f25acc2 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..8d89c86 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..6e3ca08 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..2204091 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..3e6684e --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..5129dee --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..0f0289b --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..795820b --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png Binary files differnew file mode 100644 index 0000000..830edfd --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png Binary files differnew file mode 100644 index 0000000..e342556 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png diff --git a/core/res/res/drawable-mdpi/perm_group_accessibility_features.png b/core/res/res/drawable-mdpi/perm_group_accessibility_features.png Binary files differnew file mode 100755 index 0000000..ba86d3d --- /dev/null +++ b/core/res/res/drawable-mdpi/perm_group_accessibility_features.png diff --git a/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png b/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png Binary files differnew file mode 100644 index 0000000..2fec7a3 --- /dev/null +++ b/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png diff --git a/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml deleted file mode 100644 index a0b1aaa..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml +++ /dev/null @@ -1,136 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:background="@android:color/background_dark" - > - <ScrollView - android:layout_width="match_parent" - android:layout_height="0px" - android:layout_weight="1" - android:layout_above="@+id/emergencyCallButton"> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <TextView - android:id="@+id/topHeader" - android:layout_width="match_parent" - android:layout_height="64dip" - android:layout_alignParentTop="true" - android:layout_marginStart="4dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center_vertical" - android:drawableLeft="@drawable/ic_lock_idle_lock" - android:drawablePadding="5dip" - /> - - <!-- spacer below header --> - <View - android:id="@+id/spacerTop" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_below="@id/topHeader" - android:background="@drawable/divider_horizontal_dark"/> - - <TextView - android:id="@+id/instructions" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@+id/spacerTop" - android:layout_marginTop="8dip" - android:layout_marginStart="9dip" - android:textAppearance="?android:attr/textAppearanceSmall" - android:text="@android:string/lockscreen_glogin_instructions" - /> - - <EditText - android:id="@+id/login" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/instructions" - android:layout_marginTop="8dip" - android:layout_marginStart="7dip" - android:layout_marginEnd="7dip" - android:hint="@android:string/lockscreen_glogin_username_hint" - android:inputType="textEmailAddress" - /> - - <EditText - android:id="@+id/password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/login" - android:layout_marginTop="15dip" - android:layout_marginStart="7dip" - android:layout_marginEnd="7dip" - android:inputType="textPassword" - android:hint="@android:string/lockscreen_glogin_password_hint" - android:nextFocusRight="@+id/ok" - android:nextFocusDown="@+id/ok" - /> - - <!-- ok below password, aligned to right of screen --> - <Button - android:id="@+id/ok" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/password" - android:layout_marginTop="7dip" - android:layout_marginEnd="7dip" - android:layout_alignParentEnd="true" - android:text="@android:string/lockscreen_glogin_submit_button" - /> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/ok" - android:layout_marginTop="50dip" - android:text="@android:string/lockscreen_glogin_account_recovery_hint" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center_horizontal" - /> - - </RelativeLayout> - </ScrollView> - - <!-- spacer above emergency call --> - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginBottom="4dip" - - android:background="@drawable/divider_horizontal_dark"/> - - <!-- emergency call button at bottom center --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@android:string/lockscreen_emergency_call" - android:visibility="gone" - /> - -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_lock.xml b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml deleted file mode 100644 index ea061ff..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_lock.xml +++ /dev/null @@ -1,218 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device. It is the same for landscape - and portrait.--> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:gravity="bottom" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginBottom="15dip" - android:layout_marginStart="15dip" - android:layout_marginEnd="15dip" - android:paddingTop="20dip" - android:paddingBottom="20dip" - android:background="@android:drawable/popup_full_dark" - > - - <!-- when sim is present --> - <TextView android:id="@+id/headerSimOk1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="34sp"/> - <TextView android:id="@+id/headerSimOk2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="34sp"/> - - <!-- when sim is missing / locked --> - <TextView android:id="@+id/headerSimBad1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:text="@android:string/lockscreen_missing_sim_message" - android:textAppearance="?android:attr/textAppearanceLarge"/> - <TextView android:id="@+id/headerSimBad2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="7dip" - android:layout_marginBottom="7dip" - android:gravity="center" - android:text="@android:string/lockscreen_missing_sim_instructions" - android:textAppearance="?android:attr/textAppearanceSmall"/> - - <!-- spacer after carrier info / sim messages --> - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="8dip" - android:background="@android:drawable/divider_horizontal_dark"/> - - <!-- time and date --> - <TextView android:id="@+id/time" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="34sp"/> - - <TextView android:id="@+id/date" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="18sp"/> - - <!-- spacer after time and date --> - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- battery info --> - <LinearLayout android:id="@+id/batteryInfo" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - > - - <ImageView android:id="@+id/batteryInfoIcon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:baselineAligned="true" - android:gravity="center" - /> - - <TextView android:id="@+id/batteryInfoText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:gravity="center" - /> - - </LinearLayout> - - <!-- spacer after battery info --> - <View android:id="@+id/batteryInfoSpacer" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- next alarm info --> - - <LinearLayout android:id="@+id/nextAlarmInfo" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - > - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:baselineAligned="true" - android:src="@android:drawable/ic_lock_idle_alarm" - android:gravity="center" - /> - - <TextView android:id="@+id/nextAlarmText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:gravity="center" - /> - </LinearLayout> - - <!-- spacer after alarm info --> - <View android:id="@+id/nextAlarmSpacer" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_dark"/> - - <!-- lock icon with 'screen locked' message - (shown when SIM card is present) --> - <LinearLayout android:id="@+id/screenLockedInfo" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - > - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:baselineAligned="true" - android:src="@android:drawable/ic_lock_idle_lock" - android:gravity="center" - /> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:text="@android:string/lockscreen_screen_locked" - android:gravity="center" - /> - </LinearLayout> - - <!-- message about how to unlock - (shown when SIM card is present) --> - <TextView android:id="@+id/lockInstructions" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="5dip" - android:gravity="center" - android:textSize="14sp"/> - - - <!-- emergency call button shown when sim is missing or PUKd --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="5dip" - android:layout_marginTop="5dip" - android:layout_gravity="center_horizontal" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@android:string/lockscreen_emergency_call" - /> - - </LinearLayout> -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml deleted file mode 100644 index 47d4728..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml +++ /dev/null @@ -1,184 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <!-- left side: status and music --> - <RelativeLayout - android:layout_height="match_parent" - android:layout_weight="1" - android:layout_width="0dip" - android:gravity="center"> - - <RelativeLayout android:id="@+id/transport_bg_protect" - android:layout_width="512dip" - android:layout_height="wrap_content" - android:layout_marginBottom="24dip"> - - <!-- Status --> - <include layout="@layout/keyguard_screen_status_land" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="50dip" - android:layout_marginTop="50dip" - android:layout_marginBottom="50dip" - android:layout_marginEnd="64dip" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true"/> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="match_parent" - android:layout_height="512dip" - /> - - </RelativeLayout> - - </RelativeLayout> - - <!-- right side: password --> - <RelativeLayout - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="match_parent" - android:orientation="vertical" - android:gravity="center"> - - <LinearLayout - android:orientation="vertical" - android:layout_centerInParent="true" - android:layout_width="330dip" - android:layout_height="wrap_content"> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:background="@drawable/lockscreen_password_field_dark"> - - <EditText android:id="@+id/passwordEntry" - android:layout_height="wrap_content" - android:layout_width="0dip" - android:layout_weight="1" - android:gravity="center" - android:layout_gravity="center" - android:singleLine="true" - android:textStyle="normal" - android:inputType="textPassword" - android:textSize="24sp" - android:textAppearance="?android:attr/textAppearanceMedium" - android:background="@null" - android:textColor="#ffffffff" - android:imeOptions="flagForceAscii|flagNoFullscreen|actionDone" - /> - - <!-- This delete button is only visible for numeric PIN entry --> - <ImageButton android:id="@+id/pinDel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@android:drawable/ic_input_delete" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - <!-- The IME switcher button is only shown in ASCII password mode (not PIN) --> - <ImageView android:id="@+id/switch_ime_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_ime" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - </LinearLayout> - - <!-- Numeric keyboard --> - <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" - android:layout_width="match_parent" - android:layout_height="330dip" - android:background="#40000000" - android:layout_marginTop="5dip" - android:keyBackground="@drawable/btn_keyboard_key_ics" - android:visibility="gone" - android:clickable="true" - /> - - <!-- Emergency call button. Generally not used on tablet devices. --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - style="@style/Widget.Button.Transparent" - /> - - </LinearLayout> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_width="530dip" - android:layout_height="530dip" - android:layout_centerInParent="true" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - - </RelativeLayout> - -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml deleted file mode 100644 index 6dd85cb..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml +++ /dev/null @@ -1,187 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <!-- top: status and emergency/forgot pattern buttons --> - <RelativeLayout - android:layout_height="0dip" - android:layout_weight="0.40" - android:layout_width="match_parent" - android:gravity="center"> - - <RelativeLayout android:id="@+id/transport_bg_protect" - android:layout_width="512dip" - android:layout_height="wrap_content" - android:gravity="center"> - - <!-- Status --> - <include layout="@layout/keyguard_screen_status_port" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="50dip" - android:layout_marginTop="50dip" - android:layout_marginEnd="64dip" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true"/> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="match_parent" - android:layout_height="512dip" - /> - - </RelativeLayout> - - </RelativeLayout> - - <!-- bottom: password --> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="0.60" - android:gravity="center"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_centerInParent="true" - android:orientation="vertical" - android:gravity="center"> - - <!-- Password entry field --> - <LinearLayout - android:orientation="horizontal" - android:layout_width="330dip" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginTop="120dip" - android:layout_marginBottom="5dip" - android:background="@drawable/lockscreen_password_field_dark"> - - <EditText android:id="@+id/passwordEntry" - android:layout_height="wrap_content" - android:layout_width="0dip" - android:layout_weight="1" - android:singleLine="true" - android:textStyle="normal" - android:inputType="textPassword" - android:gravity="center" - android:layout_gravity="center" - android:textSize="24sp" - android:textAppearance="?android:attr/textAppearanceMedium" - android:background="@null" - android:textColor="#ffffffff" - android:imeOptions="flagForceAscii|actionDone" - /> - - <!-- This delete button is only visible for numeric PIN entry --> - <ImageButton android:id="@+id/pinDel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@android:drawable/ic_input_delete" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - <ImageView android:id="@+id/switch_ime_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_ime" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - </LinearLayout> - - <View - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" - /> - - <!-- Numeric keyboard --> - <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" - android:layout_width="330dip" - android:layout_height="260dip" - android:background="#40000000" - android:keyBackground="@drawable/btn_keyboard_key_ics" - android:layout_marginBottom="80dip" - android:clickable="true" - /> - - <!-- emergency call button --> - <Button android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - style="@style/Widget.Button.Transparent" - /> - - </LinearLayout> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_width="440dip" - android:layout_height="440dip" - android:layout_centerInParent="true" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - - </RelativeLayout> - -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml deleted file mode 100644 index efb9e2a..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml +++ /dev/null @@ -1,122 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@android:color/background_dark" - > - - - <!-- right side --> - <!-- header text ('Enter Pin Code') --> - <TextView android:id="@+id/headerText" - android:layout_above="@+id/carrier" - android:layout_centerHorizontal="true" - android:layout_marginBottom="30dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="24sp" - /> - - <!-- Carrier info --> - <TextView android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_above="@+id/pinDisplayGroup" - android:layout_marginTop="9dip" - android:gravity="start|bottom" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - - <!-- displays dots as user enters pin --> - <LinearLayout android:id="@+id/pinDisplayGroup" - android:orientation="horizontal" - android:layout_centerInParent="true" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:addStatesFromChildren="true" - android:gravity="center_vertical" - android:baselineAligned="false" - android:paddingEnd="0dip" - android:layout_marginEnd="30dip" - android:layout_marginStart="30dip" - android:background="@android:drawable/edit_text" - > - - <EditText android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="match_parent" - android:maxLines="1" - android:background="@null" - android:textSize="32sp" - android:inputType="textPassword" - /> - - <ImageButton android:id="@+id/backspace" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginTop="2dip" - android:layout_marginEnd="2dip" - android:layout_marginBottom="2dip" - android:gravity="center" - /> - - </LinearLayout> - - <LinearLayout - android:orientation="horizontal" - android:layout_alignParentBottom="true" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:layout_marginEnd="8dip"> - - <Button android:id="@+id/emergencyCallButton" - android:text="@android:string/lockscreen_emergency_call" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginEnd="8dip" - android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - /> - - <Button android:id="@+id/ok" - android:text="@android:string/ok" - android:layout_alignParentBottom="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:textSize="18sp" - /> - </LinearLayout> - -</RelativeLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml deleted file mode 100644 index db84156..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml +++ /dev/null @@ -1,120 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:background="@android:color/background_dark" - android:gravity="center_horizontal"> - - <LinearLayout android:id="@+id/topDisplayGroup" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <!-- header text ('Enter Pin Code') --> - <TextView android:id="@+id/headerText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceLarge"/> - - <!-- Carrier info --> - <TextView android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="9dip" - android:gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium"/> - - <!-- password entry --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:gravity="center_vertical" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters pin --> - <EditText android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textAppearance="?android:attr/textAppearanceLargeInverse" - android:textColor="@android:color/primary_text_holo_light" - android:textStyle="bold" - android:inputType="textPassword" - /> - - <ImageButton android:id="@+id/backspace" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - - </LinearLayout> - - <include - android:id="@+id/keyPad" - layout="@android:layout/twelve_key_entry" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/topDisplayGroup" - android:layout_marginTop="10dip" - /> - - <!-- spacer below keypad --> - <View - android:id="@+id/spacerBottom" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="6dip" - android:layout_above="@id/emergencyCallButton" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- The emergency button should take the rest of the space and be centered vertically --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" - android:gravity="center" - android:orientation="vertical"> - - <!-- emergency call button --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@android:drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@android:string/lockscreen_emergency_call" - /> - </LinearLayout> - -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml deleted file mode 100644 index c6ddd1b..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2010, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- Status to show on the left side of lock screen --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="end"> - - <TextView - android:id="@+id/carrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="16sp" - android:drawablePadding="4dip" - android:layout_marginTop="32dip" - android:singleLine="true" - android:ellipsize="marquee" - android:visibility="gone" - /> - - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - > - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_pattern_unlock_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/lockscreen_clock_background" - android:layout_marginBottom="6dip" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_pattern_unlock_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/lockscreen_clock_foreground" - android:layout_alignStart="@id/timeDisplayBackground" - android:layout_alignTop="@id/timeDisplayBackground" - android:layout_marginBottom="6dip" - /> - - </com.android.internal.widget.DigitalClock> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/time" - android:layout_marginTop="10dip"> - - <TextView - android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="16sp"/> - - <TextView - android:id="@+id/alarm_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="16dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:drawablePadding="4dip" - android:textSize="16sp"/> - - </LinearLayout> - - <!-- Status1 is generally battery status and informational messages --> - <TextView - android:id="@+id/status1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dip" - android:textSize="16sp" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - - <TextView - android:id="@+id/owner_info" - android:lineSpacingExtra="8dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="16sp" - android:layout_marginTop="20dip" - android:singleLine="false" - android:textColor="@color/lockscreen_owner_info" - android:visibility="invisible" - /> -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml deleted file mode 100644 index 765dc95..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2010, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- Status to show on the left side of lock screen --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="20dip" - android:gravity="end" - > - - <TextView - android:id="@+id/carrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="16sp" - android:drawablePadding="4dip" - android:layout_marginTop="32dip" - android:singleLine="true" - android:ellipsize="marquee" - android:visibility="gone" - /> - - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_pattern_unlock_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/lockscreen_clock_background" - android:layout_marginBottom="6dip" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_pattern_unlock_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/lockscreen_clock_foreground" - android:layout_marginBottom="6dip" - android:layout_alignStart="@id/timeDisplayBackground" - android:layout_alignTop="@id/timeDisplayBackground" - /> - - </com.android.internal.widget.DigitalClock> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/time" - android:layout_marginTop="10dip"> - - <TextView - android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="16sp"/> - - <TextView - android:id="@+id/alarm_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="16dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:drawablePadding="4dip" - android:textSize="16sp"/> - - </LinearLayout> - - <TextView - android:id="@+id/status1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dip" - android:textSize="16sp" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - - <TextView - android:id="@+id/owner_info" - android:lineSpacingExtra="8dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="20dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="16sp" - android:singleLine="false" - android:visibility="invisible" - android:textColor="@color/lockscreen_owner_info" - /> -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml deleted file mode 100644 index 4f6b62a..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml +++ /dev/null @@ -1,124 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device. It is the same for landscape - and portrait.--> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:id="@+id/root"> - - <!-- top: status --> - <RelativeLayout - android:layout_height="0dip" - android:layout_weight="0.42" - android:layout_width="match_parent" - android:gravity="center"> - - <RelativeLayout android:id="@+id/transport_bg_protect" - android:layout_width="512dip" - android:layout_height="wrap_content" - android:gravity="center"> - - <!-- Status --> - <include layout="@layout/keyguard_screen_status_port" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="50dip" - android:layout_marginTop="50dip" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true"/> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="match_parent" - android:layout_height="512dip" - /> - - </RelativeLayout> - - </RelativeLayout> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="0.58" - android:orientation="vertical" - android:gravity="bottom"> - - <TextView - android:id="@+id/screenLocked" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="24dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginTop="12dip" - android:drawablePadding="4dip" - /> - - <com.android.internal.widget.multiwaveview.GlowPadView - android:id="@+id/unlock_widget" - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:gravity="center" - android:focusable="true" - - android:targetDrawables="@array/lockscreen_targets_with_camera" - android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera" - android:directionDescriptions="@array/lockscreen_direction_descriptions" - android:handleDrawable="@drawable/ic_lockscreen_handle" - android:outerRingDrawable="@drawable/ic_lockscreen_outerring" - android:outerRadius="@dimen/glowpadview_target_placement_radius" - android:innerRadius="@dimen/glowpadview_inner_radius" - android:snapMargin="@dimen/glowpadview_snap_margin" - android:feedbackCount="1" - android:vibrationDuration="20" - android:glowRadius="@dimen/glowpadview_glow_radius" - android:pointDrawable="@drawable/ic_lockscreen_glowdot" - /> - - <!-- emergency call button shown when sim is PUKd and tab_selector is hidden --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@drawable/ic_emergency" - android:layout_centerInParent="true" - android:layout_alignParentBottom="true" - android:layout_marginBottom="90dip" - style="@style/Widget.Button.Transparent" - android:drawablePadding="8dip" - android:visibility="gone" - /> - </LinearLayout> - -</LinearLayout> - diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml deleted file mode 100644 index d5201ec..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml +++ /dev/null @@ -1,124 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device.--> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal" - android:id="@+id/root"> - - <!-- left side: status and music --> - <RelativeLayout - android:layout_height="match_parent" - android:layout_weight="0.42" - android:layout_width="0dip" - android:gravity="center"> - - <RelativeLayout android:id="@+id/transport_bg_protect" - android:layout_width="512dip" - android:layout_height="wrap_content"> - - <!-- Status --> - <include layout="@layout/keyguard_screen_status_land" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="50dip" - android:layout_marginTop="50dip" - android:layout_marginEnd="64dip" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true"/> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="match_parent" - android:layout_height="512dip" - /> - - </RelativeLayout> - - </RelativeLayout> - - <!-- right side --> - <RelativeLayout - android:layout_height="match_parent" - android:layout_weight="0.58" - android:layout_width="0dip" - android:gravity="center_horizontal|center_vertical"> - - <TextView - android:id="@+id/screenLocked" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center" - android:layout_marginTop="12dip" - android:layout_alignParentStart="true" - android:layout_alignParentTop="true" - android:drawablePadding="4dip"/> - - <com.android.internal.widget.multiwaveview.GlowPadView - android:id="@+id/unlock_widget" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_rowSpan="7" - android:layout_gravity="center_vertical|end" - android:gravity="center" - android:focusable="true" - - android:targetDrawables="@array/lockscreen_targets_with_camera" - android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera" - android:directionDescriptions="@array/lockscreen_direction_descriptions" - android:handleDrawable="@drawable/ic_lockscreen_handle" - android:outerRingDrawable="@drawable/ic_lockscreen_outerring" - android:outerRadius="@dimen/glowpadview_target_placement_radius" - android:innerRadius="@dimen/glowpadview_inner_radius" - android:snapMargin="@dimen/glowpadview_snap_margin" - android:feedbackCount="1" - android:vibrationDuration="20" - android:glowRadius="@dimen/glowpadview_glow_radius" - android:pointDrawable="@drawable/ic_lockscreen_glowdot" - /> - - <!-- emergency call button shown when sim is PUKd and tab_selector is hidden --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="80dip" - android:layout_marginBottom="80dip" - android:layout_alignParentEnd="true" - android:layout_alignParentBottom="true" - android:drawableLeft="@drawable/ic_emergency" - style="@style/Widget.Button.Transparent" - android:drawablePadding="8dip" - android:visibility="gone"/> - - </RelativeLayout> - -</LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml deleted file mode 100644 index a71ef30..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml +++ /dev/null @@ -1,157 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the screen that shows the 9 circle unlock widget and instructs - the user how to unlock their device, or make an emergency call. This - is the portrait layout. --> - -<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <!-- left side: status and music --> - <RelativeLayout - android:layout_height="match_parent" - android:layout_weight="1" - android:layout_width="0dip" - android:gravity="center"> - - <RelativeLayout android:id="@+id/transport_bg_protect" - android:layout_width="512dip" - android:layout_height="wrap_content" - android:layout_marginBottom="24dip"> - - <!-- Status --> - <include layout="@layout/keyguard_screen_status_land" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="50dip" - android:layout_marginTop="50dip" - android:layout_marginBottom="50dip" - android:layout_marginEnd="64dip" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true"/> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="match_parent" - android:layout_height="512dip" - /> - - </RelativeLayout> - - </RelativeLayout> - - <!-- right side: lock pattern --> - <RelativeLayout - android:layout_weight="1" - android:layout_width="0dip" - android:layout_height="match_parent" - android:gravity="center_vertical|center_horizontal"> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - android:gravity="center_vertical|center_horizontal"> - - <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" - android:layout_width="354dip" - android:layout_height="354dip" - android:layout_gravity="center_vertical" - /> - - <!-- Emergency and forgot pattern buttons. --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_below="@id/lockPattern" - android:layout_alignStart="@id/lockPattern" - android:layout_alignEnd="@id/lockPattern" - android:layout_marginTop="28dip" - style="?android:attr/buttonBarStyle" - android:gravity="center" - android:weightSum="2"> - - <Button android:id="@+id/forgotPatternButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/lockscreen_forgot_password_button" - android:drawablePadding="8dip" - android:text="@string/lockscreen_forgot_pattern_button_text" - android:visibility="gone" - /> - - <Button android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - /> - - </LinearLayout> - - </RelativeLayout> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_width="530dip" - android:layout_height="530dip" - android:layout_centerInParent="true" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - - </RelativeLayout> - -</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml deleted file mode 100644 index 0c4a2c7..0000000 --- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml +++ /dev/null @@ -1,150 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <!-- top: status --> - <RelativeLayout - android:layout_height="0dip" - android:layout_weight="0.40" - android:layout_width="match_parent" - android:gravity="center"> - - <RelativeLayout android:id="@+id/transport_bg_protect" - android:layout_width="512dip" - android:layout_height="wrap_content" - android:gravity="center"> - - <!-- Status --> - <include layout="@layout/keyguard_screen_status_land" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="50dip" - android:layout_marginTop="50dip" - android:layout_marginEnd="64dip" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true"/> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="match_parent" - android:layout_height="512dip" - /> - - </RelativeLayout> - - </RelativeLayout> - - <!-- bottom: lock pattern, emergency dialer and forgot pattern button --> - <RelativeLayout - android:layout_weight="0.60" - android:layout_width="match_parent" - android:layout_height="0dip" - android:gravity="center"> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - android:gravity="center"> - - <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" - android:layout_width="354dip" - android:layout_height="354dip" - /> - - <!-- Emergency and forgot pattern buttons. --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_below="@id/lockPattern" - android:layout_alignStart="@id/lockPattern" - android:layout_alignEnd="@id/lockPattern" - style="?android:attr/buttonBarStyle" - android:gravity="center" - android:weightSum="2"> - - <Button android:id="@+id/forgotPatternButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/lockscreen_forgot_password_button" - android:drawablePadding="8dip" - android:text="@string/lockscreen_forgot_pattern_button_text" - android:visibility="gone" - /> - - <Button android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - /> - - </LinearLayout> - - </RelativeLayout> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_width="440dip" - android:layout_height="440dip" - android:layout_centerInParent="true" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - - </RelativeLayout> - -</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> diff --git a/core/res/res/layout/keyguard_screen_glogin_unlock.xml b/core/res/res/layout/keyguard_screen_glogin_unlock.xml deleted file mode 100644 index db920b4..0000000 --- a/core/res/res/layout/keyguard_screen_glogin_unlock.xml +++ /dev/null @@ -1,126 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:background="@android:color/background_dark" - > - <ScrollView - android:layout_width="match_parent" - android:layout_height="0px" - android:layout_weight="1" - android:layout_above="@+id/emergencyCallButton"> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - > - - <TextView - android:id="@+id/topHeader" - android:layout_width="match_parent" - android:layout_height="64dip" - android:layout_alignParentTop="true" - android:layout_marginStart="4dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center_vertical" - android:drawableLeft="@drawable/ic_lock_idle_lock" - android:drawablePadding="5dip" - /> - - <!-- spacer below header --> - <View - android:id="@+id/spacerTop" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_below="@id/topHeader" - android:background="@drawable/divider_horizontal_dark"/> - - <TextView - android:id="@+id/instructions" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@+id/spacerTop" - android:layout_marginTop="8dip" - android:layout_marginStart="9dip" - android:textAppearance="?android:attr/textAppearanceSmall" - android:text="@android:string/lockscreen_glogin_instructions" - /> - - <EditText - android:id="@+id/login" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/instructions" - android:layout_marginTop="8dip" - android:layout_marginStart="7dip" - android:layout_marginEnd="7dip" - android:hint="@android:string/lockscreen_glogin_username_hint" - android:inputType="textEmailAddress" - /> - - <EditText - android:id="@+id/password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/login" - android:layout_marginTop="15dip" - android:layout_marginStart="7dip" - android:layout_marginEnd="7dip" - android:inputType="textPassword" - android:hint="@android:string/lockscreen_glogin_password_hint" - android:nextFocusRight="@+id/ok" - android:nextFocusDown="@+id/ok" - /> - - <!-- ok below password, aligned to right of screen --> - <Button - android:id="@+id/ok" - android:layout_width="85dip" - android:layout_height="wrap_content" - android:layout_below="@id/password" - android:layout_marginTop="7dip" - android:layout_marginEnd="7dip" - android:layout_alignParentEnd="true" - android:text="@android:string/lockscreen_glogin_submit_button" - /> - - </RelativeLayout> - </ScrollView> - - <!-- spacer above emergency call --> - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginBottom="4dip" - - android:background="@drawable/divider_horizontal_dark"/> - - <!-- emergency call button at bottom center --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="4dip" - android:text="@android:string/lockscreen_emergency_call" - /> - -</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_lock.xml b/core/res/res/layout/keyguard_screen_lock.xml deleted file mode 100644 index 8c178b1..0000000 --- a/core/res/res/layout/keyguard_screen_lock.xml +++ /dev/null @@ -1,219 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device. It is the same for landscape - and portrait.--> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:gravity="bottom" - android:layout_width="match_parent" - android:layout_height="match_parent" - > - - <LinearLayout - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginBottom="15dip" - android:layout_marginStart="15dip" - android:layout_marginEnd="15dip" - android:paddingTop="20dip" - android:paddingBottom="20dip" - android:background="@android:drawable/popup_full_dark" - > - - <!-- when sim is present --> - <TextView android:id="@+id/headerSimOk1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="34sp"/> - <TextView android:id="@+id/headerSimOk2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="34sp"/> - - <!-- when sim is missing / locked --> - <TextView android:id="@+id/headerSimBad1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:text="@android:string/lockscreen_missing_sim_message" - android:textAppearance="?android:attr/textAppearanceLarge"/> - <TextView android:id="@+id/headerSimBad2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="7dip" - android:layout_marginBottom="7dip" - android:gravity="center" - android:text="@android:string/lockscreen_missing_sim_instructions" - android:textAppearance="?android:attr/textAppearanceSmall"/> - - <!-- spacer after carrier info / sim messages --> - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="8dip" - android:background="@android:drawable/divider_horizontal_dark"/> - - <!-- time and date --> - <TextView android:id="@+id/time" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="34sp"/> - - <TextView android:id="@+id/date" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="18sp"/> - - <!-- spacer after time and date --> - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- battery info --> - <LinearLayout android:id="@+id/batteryInfo" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - > - - <ImageView android:id="@+id/batteryInfoIcon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:baselineAligned="true" - android:gravity="center" - /> - - <TextView android:id="@+id/batteryInfoText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:gravity="center" - /> - - </LinearLayout> - - <!-- spacer after battery info --> - <View android:id="@+id/batteryInfoSpacer" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- next alarm info --> - - <LinearLayout android:id="@+id/nextAlarmInfo" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - > - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:baselineAligned="true" - android:src="@android:drawable/ic_lock_idle_alarm" - android:gravity="center" - /> - - <TextView android:id="@+id/nextAlarmText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:gravity="center" - /> - </LinearLayout> - - <!-- spacer after alarm info --> - <View android:id="@+id/nextAlarmSpacer" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_dark"/> - - <!-- lock icon with 'screen locked' message - (shown when SIM card is present) --> - <LinearLayout android:id="@+id/screenLockedInfo" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - > - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:baselineAligned="true" - android:src="@android:drawable/ic_lock_idle_lock" - android:gravity="center" - /> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:text="@android:string/lockscreen_screen_locked" - android:gravity="center" - /> - </LinearLayout> - - <!-- message about how to unlock - (shown when SIM card is present) --> - <TextView android:id="@+id/lockInstructions" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="5dip" - android:gravity="center" - android:textSize="14sp"/> - - - <!-- emergency call button shown when sim is missing or PUKd --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="5dip" - android:layout_marginTop="5dip" - android:layout_gravity="center_horizontal" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="4dip" - android:text="@android:string/lockscreen_emergency_call" - /> - - </LinearLayout> -</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml deleted file mode 100644 index 80d9d61..0000000 --- a/core/res/res/layout/keyguard_screen_password_landscape.xml +++ /dev/null @@ -1,241 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device.--> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:rowCount="8" - android:id="@+id/root" - android:clipChildren="false" - android:rowOrderPreserved="false"> - - <!-- Column 0 --> - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - android:layout_gravity="end" - android:layout_rowSpan="2"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_background" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_foreground" - android:layout_alignStart="@id/timeDisplayBackground" - android:layout_alignTop="@id/timeDisplayBackground" - /> - - </com.android.internal.widget.DigitalClock> - - <TextView - android:id="@+id/date" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:layout_below="@id/time" - android:layout_marginTop="6dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <TextView - android:id="@+id/alarm_status" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - android:layout_marginTop="4dip" - android:layout_gravity="end" - /> - - <TextView - android:id="@+id/status1" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:layout_marginTop="4dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - <Space android:layout_gravity="fill" /> - - <TextView - android:id="@+id/carrier" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:textColor="?android:attr/textColorSecondary" - /> - - <Button - android:id="@+id/emergencyCallButton" - android:layout_gravity="end" - android:drawableLeft="@drawable/lockscreen_emergency_button" - android:text="@string/lockscreen_emergency_call" - style="?android:attr/buttonBarButtonStyle" - android:drawablePadding="8dip" - android:visibility="visible" - /> - - <!-- Column 1 --> - <Space - android:layout_width="16dip" - android:layout_rowSpan="8" - android:layout_gravity="fill_vertical" /> - - <!-- Column 2 - password entry field and PIN keyboard --> - <LinearLayout - android:orientation="horizontal" - android:layout_width="270dip" - android:layout_gravity="center_vertical" - android:background="@drawable/lockscreen_password_field_dark"> - - <EditText android:id="@+id/passwordEntry" - android:layout_height="wrap_content" - android:layout_width="0dip" - android:layout_weight="1" - android:gravity="center" - android:layout_gravity="center_vertical" - android:singleLine="true" - android:textStyle="normal" - android:inputType="textPassword" - android:textSize="24sp" - android:minEms="8" - android:textAppearance="?android:attr/textAppearanceMedium" - android:background="@null" - android:textColor="?android:attr/textColorPrimary" - android:imeOptions="flagForceAscii|flagNoFullscreen|actionDone" - /> - - <!-- This delete button is only visible for numeric PIN entry --> - <ImageButton android:id="@+id/pinDel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@android:drawable/ic_input_delete" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - <ImageView android:id="@+id/switch_ime_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_ime" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - </LinearLayout> - - <!-- Numeric keyboard --> - <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" - android:layout_width="270dip" - android:layout_height="wrap_content" - android:layout_marginStart="4dip" - android:layout_marginEnd="4dip" - android:background="#40000000" - android:layout_marginTop="5dip" - android:keyBackground="@*android:drawable/btn_keyboard_key_ics" - android:visibility="gone" - android:layout_rowSpan="7" - android:clickable="true" - /> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="6" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - /> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_row="0" - android:layout_column="2" - android:layout_rowSpan="8" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - -</GridLayout> diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml deleted file mode 100644 index 3d61bae..0000000 --- a/core/res/res/layout/keyguard_screen_password_portrait.xml +++ /dev/null @@ -1,231 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<GridLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center_horizontal"> - - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginBottom="18dip" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin" - android:layout_gravity="end"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@*android:color/lockscreen_clock_background" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_foreground" - /> - - </com.android.internal.widget.DigitalClock> - - <LinearLayout - android:orientation="horizontal" - android:layout_gravity="end" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin"> - - <TextView - android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <TextView - android:id="@+id/alarm_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="16dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - </LinearLayout> - - <TextView - android:id="@+id/status1" - android:layout_gravity="end" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - android:paddingBottom="4dip" - /> - - <!-- Password entry field --> - <!-- Note: the entire container is styled to look like the edit field, - since the backspace/IME switcher looks better inside --> - <LinearLayout - android:layout_gravity="center_vertical|fill_horizontal" - android:orientation="horizontal" - android:background="@drawable/lockscreen_password_field_dark" - android:layout_marginStart="16dip" - android:layout_marginEnd="16dip"> - - <EditText android:id="@+id/passwordEntry" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_horizontal" - android:layout_gravity="center_vertical" - android:singleLine="true" - android:textStyle="normal" - android:inputType="textPassword" - android:textSize="36sp" - android:background="@null" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="#ffffffff" - android:imeOptions="flagForceAscii|actionDone" - /> - - <!-- This delete button is only visible for numeric PIN entry --> - <ImageButton android:id="@+id/pinDel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@android:drawable/ic_input_delete" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center_vertical" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - <ImageView android:id="@+id/switch_ime_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_ime" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> - - </LinearLayout> - - <!-- Numeric keyboard --> - <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" - android:layout_width="match_parent" - android:layout_marginStart="4dip" - android:layout_marginEnd="4dip" - android:paddingTop="4dip" - android:paddingBottom="4dip" - android:background="#40000000" - android:keyBackground="@*android:drawable/btn_keyboard_key_ics" - android:visibility="gone" - android:clickable="true" - /> - - <TextView - android:id="@+id/carrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - android:singleLine="true" - android:ellipsize="marquee" - android:paddingTop="4dip" - /> - - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="4dip" - android:layout_gravity="center_horizontal" - android:drawableLeft="@*android:drawable/lockscreen_emergency_button" - style="?android:attr/buttonBarButtonStyle" - android:drawablePadding="4dip" - android:text="@*android:string/lockscreen_emergency_call" - android:visibility="gone" - /> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="3" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - /> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_row="3" - android:layout_column="0" - android:layout_rowSpan="2" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - -</GridLayout> diff --git a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml deleted file mode 100644 index 3738766..0000000 --- a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml +++ /dev/null @@ -1,120 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@android:color/background_dark" - > - - <!-- header text ('Enter Pin Code') --> - <TextView android:id="@+id/headerText" - android:layout_above="@+id/carrier" - android:layout_centerHorizontal="true" - android:layout_marginBottom="30dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="24sp" - /> - - <!-- Carrier info --> - <TextView android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_above="@+id/pinDisplayGroup" - android:layout_marginTop="9dip" - android:gravity="start|bottom" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - - <!-- displays dots as user enters pin --> - <LinearLayout android:id="@+id/pinDisplayGroup" - android:orientation="horizontal" - android:layout_centerInParent="true" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:addStatesFromChildren="true" - android:gravity="center_vertical" - android:baselineAligned="false" - android:paddingEnd="0dip" - android:layout_marginEnd="30dip" - android:layout_marginStart="30dip" - android:background="@android:drawable/edit_text" - > - - <EditText android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="match_parent" - android:maxLines="1" - android:background="@null" - android:textSize="32sp" - android:inputType="textPassword" - /> - - <ImageButton android:id="@+id/backspace" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginTop="2dip" - android:layout_marginEnd="2dip" - android:layout_marginBottom="2dip" - android:gravity="center" - /> - - </LinearLayout> - - <LinearLayout - android:orientation="horizontal" - android:layout_alignParentBottom="true" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:layout_marginEnd="8dip"> - - <Button android:id="@+id/emergencyCallButton" - android:text="@android:string/lockscreen_emergency_call" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginEnd="8dip" - android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="4dip" - /> - - <Button android:id="@+id/ok" - android:text="@android:string/ok" - android:layout_alignParentBottom="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:textSize="18sp" - /> - </LinearLayout> - -</RelativeLayout> diff --git a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml deleted file mode 100644 index 4c42a17..0000000 --- a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml +++ /dev/null @@ -1,119 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:background="@android:color/background_dark" - android:gravity="center_horizontal"> - - <LinearLayout android:id="@+id/topDisplayGroup" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <!-- header text ('Enter Pin Code') --> - <TextView android:id="@+id/headerText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textAppearance="?android:attr/textAppearanceLarge"/> - - <!-- Carrier info --> - <TextView android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="9dip" - android:gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium"/> - - <!-- password entry --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:gravity="center_vertical" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters pin --> - <EditText android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textAppearance="?android:attr/textAppearanceLargeInverse" - android:textColor="@android:color/primary_text_holo_light" - android:textStyle="bold" - android:inputType="textPassword" - /> - - <ImageButton android:id="@+id/backspace" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - - </LinearLayout> - - <include - android:id="@+id/keyPad" - layout="@android:layout/twelve_key_entry" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/topDisplayGroup" - android:layout_marginTop="10dip" - /> - - <!-- spacer below keypad --> - <View - android:id="@+id/spacerBottom" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="6dip" - android:layout_above="@id/emergencyCallButton" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- The emergency button should take the rest of the space and be centered vertically --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" - android:gravity="center" - android:orientation="vertical"> - - <!-- emergency call button --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@android:drawable/ic_emergency" - android:drawablePadding="4dip" - android:text="@android:string/lockscreen_emergency_call" - /> - </LinearLayout> - -</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml deleted file mode 100644 index fd6dc26..0000000 --- a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml +++ /dev/null @@ -1,184 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@android:color/background_dark" - > - - <LinearLayout android:id="@+id/topDisplayGroup" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <!-- header text ('Enter Puk Code') --> - <TextView android:id="@+id/headerText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceLarge"/> - - <!-- Carrier info --> - <TextView android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="9dip" - android:gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium"/> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginEnd="10dip" - android:layout_marginStart="10dip"> - <TextView android:id="@+id/enter_puk" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:text="@android:string/keyguard_password_enter_puk_prompt" - android:textSize="30sp" - android:layout_marginBottom="10dip"/> - <TextView android:id="@+id/enter_pin" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:text="@android:string/keyguard_password_enter_pin_prompt" - android:textSize="30sp" - android:layout_marginTop="10dip"/> - </LinearLayout> - - <LinearLayout - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_weight="1" - android:layout_height="match_parent" - android:paddingEnd="0dip" - android:layout_marginEnd="10dip" - android:layout_marginStart="10dip"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:gravity="center_vertical" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters puk --> - <TextView android:id="@+id/pukDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textAppearance="?android:attr/textAppearanceLargeInverse" - android:textStyle="bold" - android:inputType="textPassword" - /> - - <ImageButton android:id="@+id/pukDel" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:gravity="center_vertical" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters new pin --> - <EditText android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textAppearance="?android:attr/textAppearanceLargeInverse" - android:textColor="@android:color/primary_text_holo_light" - android:textStyle="bold" - android:inputType="textPassword" - android:hint="@android:string/keyguard_password_enter_pin_prompt" - /> - - <ImageButton android:id="@+id/pinDel" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - </LinearLayout> - </LinearLayout> - </LinearLayout> - - <LinearLayout - android:orientation="horizontal" - android:layout_alignParentBottom="true" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:layout_marginEnd="8dip"> - - <Button android:id="@+id/emergencyCallButton" - android:text="@android:string/lockscreen_emergency_call" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginEnd="8dip" - android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="4dip" - /> - - <Button android:id="@+id/ok" - android:text="@android:string/ok" - android:layout_alignParentBottom="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:textSize="18sp" - /> - </LinearLayout> - -</RelativeLayout> diff --git a/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml b/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml deleted file mode 100644 index 5397e62..0000000 --- a/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml +++ /dev/null @@ -1,170 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:background="@android:color/background_dark" - android:gravity="center_horizontal"> - - <LinearLayout android:id="@+id/topDisplayGroup" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <!-- header text ('Enter Puk Code') --> - <TextView android:id="@+id/headerText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:textAppearance="?android:attr/textAppearanceLarge"/> - - <!-- Carrier info --> - <TextView android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="9dip" - android:gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:textAppearance="?android:attr/textAppearanceMedium"/> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_weight="1" - android:layout_height="match_parent" - android:paddingEnd="0dip" - android:layout_marginEnd="10dip" - android:layout_marginStart="10dip"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:gravity="center_vertical" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters puk --> - <EditText android:id="@+id/pukDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textStyle="bold" - android:inputType="textPassword" - android:textColor="#000" - android:hint="@android:string/keyguard_password_enter_puk_prompt" - /> - - <ImageButton android:id="@+id/pukDel" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="6dip" - android:layout_marginStart="6dip" - android:gravity="center_vertical" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters new pin --> - <EditText android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textStyle="bold" - android:inputType="textPassword" - android:textColor="#000" - android:hint="@android:string/keyguard_password_enter_pin_prompt" - /> - - <ImageButton android:id="@+id/pinDel" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - </LinearLayout> - </LinearLayout> - </LinearLayout> - - <include - android:id="@+id/keyPad" - layout="@android:layout/twelve_key_entry" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/topDisplayGroup" - android:layout_marginTop="10dip" - /> - - <!-- spacer below keypad --> - <View - android:id="@+id/spacerBottom" - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="6dip" - android:layout_above="@id/emergencyCallButton" - android:background="@android:drawable/divider_horizontal_dark" - /> - - <!-- The emergency button should take the rest of the space and be centered vertically --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" - android:gravity="center" - android:orientation="vertical"> - - <!-- emergency call button --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@android:drawable/ic_emergency" - android:drawablePadding="4dip" - android:text="@android:string/lockscreen_emergency_call" - /> - </LinearLayout> - -</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml deleted file mode 100644 index 54381ee..0000000 --- a/core/res/res/layout/keyguard_screen_tab_unlock.xml +++ /dev/null @@ -1,200 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device. It is the same for landscape - and portrait.--> -<GridLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center_horizontal"> - - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="@dimen/keyguard_lockscreen_status_line_clockfont_top_margin" - android:layout_marginBottom="12dip" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin" - android:layout_gravity="end"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_background" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_foreground" - android:layout_alignStart="@id/timeDisplayBackground" - android:layout_alignTop="@id/timeDisplayBackground" - /> - - </com.android.internal.widget.DigitalClock> - - <LinearLayout - android:orientation="horizontal" - android:layout_gravity="end" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin"> - - <TextView - android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <TextView - android:id="@+id/alarm_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="16dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - </LinearLayout> - - <TextView - android:id="@+id/status1" - android:layout_gravity="end" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - <Space android:layout_gravity="fill" /> - - <!-- emergency call button shown when sim is PUKd and tab_selector is hidden --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="4dip" - android:layout_marginEnd="16dip" - android:layout_gravity="end" - android:drawableLeft="@*android:drawable/lockscreen_emergency_button" - style="?android:attr/buttonBarButtonStyle" - android:drawablePadding="4dip" - android:text="@*android:string/lockscreen_emergency_call" - android:visibility="gone" - /> - - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="302dip"> - - <com.android.internal.widget.multiwaveview.GlowPadView - android:id="@+id/unlock_widget" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_alignParentBottom="true" - android:gravity="top" - android:focusable="true" - - android:targetDrawables="@array/lockscreen_targets_with_camera" - android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera" - android:directionDescriptions="@array/lockscreen_direction_descriptions" - android:handleDrawable="@drawable/ic_lockscreen_handle" - android:outerRingDrawable="@drawable/ic_lockscreen_outerring" - android:outerRadius="@dimen/glowpadview_target_placement_radius" - android:innerRadius="@dimen/glowpadview_inner_radius" - android:snapMargin="@dimen/glowpadview_snap_margin" - android:feedbackCount="1" - android:vibrationDuration="20" - android:glowRadius="@dimen/glowpadview_glow_radius" - android:pointDrawable="@drawable/ic_lockscreen_glowdot" - /> - - <TextView - android:id="@+id/carrier" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - android:layout_marginBottom="12dip" - android:gravity="center_horizontal" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:textColor="?android:attr/textColorSecondary" - /> - - </RelativeLayout> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - style="?android:attr/buttonBarStyle" - android:gravity="center" - android:weightSum="2"> - - <Button android:id="@+id/emergencyCallButton" - android:layout_gravity="center_horizontal" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - style="?android:attr/buttonBarButtonStyle" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:text="@*android:string/lockscreen_emergency_call" - android:drawableLeft="@*android:drawable/lockscreen_emergency_button" - android:drawablePadding="0dip" - android:visibility="gone" - /> - - </LinearLayout> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="4" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - /> - -</GridLayout> - diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml deleted file mode 100644 index 7ef9d8b..0000000 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the general lock screen which shows information about the - state of the device, as well as instructions on how to get past it - depending on the state of the device.--> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:rowCount="7" - android:id="@+id/root" - android:clipChildren="false"> - - <!-- Column 0 --> - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="80dip" - android:layout_marginBottom="8dip" - android:layout_gravity="end"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_background" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_foreground" - android:layout_alignStart="@id/timeDisplayBackground" - android:layout_alignTop="@id/timeDisplayBackground" - /> - - </com.android.internal.widget.DigitalClock> - - <TextView - android:id="@+id/date" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:layout_marginTop="6dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <TextView - android:id="@+id/alarm_status" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - android:layout_marginTop="4dip" - android:layout_gravity="end" - /> - - <TextView - android:id="@+id/status1" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:layout_marginTop="4dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - <Space android:layout_gravity="fill" /> - - <TextView - android:id="@+id/carrier" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:layout_marginBottom="12dip" - android:gravity="end" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:textColor="?android:attr/textColorSecondary" - /> - - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="end" - android:drawableLeft="@*android:drawable/lockscreen_emergency_button" - android:text="@*android:string/lockscreen_emergency_call" - style="?android:attr/buttonBarButtonStyle" - android:drawablePadding="8dip" - android:visibility="gone" - /> - - <!-- Column 1 --> - <Space android:layout_width="64dip" android:layout_rowSpan="7" /> - - <!-- Column 2 --> - <com.android.internal.widget.multiwaveview.GlowPadView - android:id="@+id/unlock_widget" - android:layout_width="302dip" - android:layout_height="match_parent" - android:layout_rowSpan="7" - android:gravity="start|center_vertical" - android:focusable="true" - - android:targetDrawables="@array/lockscreen_targets_with_camera" - android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera" - android:directionDescriptions="@array/lockscreen_direction_descriptions" - android:handleDrawable="@drawable/ic_lockscreen_handle" - android:outerRingDrawable="@drawable/ic_lockscreen_outerring" - android:outerRadius="@dimen/glowpadview_target_placement_radius" - android:innerRadius="@dimen/glowpadview_inner_radius" - android:snapMargin="@dimen/glowpadview_snap_margin" - android:feedbackCount="1" - android:vibrationDuration="20" - android:glowRadius="@dimen/glowpadview_glow_radius" - android:pointDrawable="@drawable/ic_lockscreen_glowdot" - /> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="5" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - /> - -</GridLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml deleted file mode 100644 index 27f6629..0000000 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ /dev/null @@ -1,196 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the screen that shows the 9 circle unlock widget and instructs - the user how to unlock their device, or make an emergency call. This - is the portrait layout. --> - -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/root" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:rowCount="7"> - - <!-- Column 0: Time, date and status --> - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="8dip" - android:layout_marginBottom="12dip" - android:layout_gravity="end"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:layout_marginBottom="6dip" - android:textColor="@color/lockscreen_clock_background" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:layout_marginBottom="6dip" - android:layout_alignStart="@id/timeDisplayBackground" - android:layout_alignTop="@id/timeDisplayBackground" - android:textColor="@color/lockscreen_clock_foreground" - /> - - </com.android.internal.widget.DigitalClock> - - <TextView - android:id="@+id/date" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <TextView - android:id="@+id/alarm_status" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:layout_gravity="end" - android:drawablePadding="4dip" - /> - - <TextView - android:id="@+id/status1" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <Space android:layout_gravity="fill" /> - - <TextView android:id="@+id/carrier" - android:layout_width="0dip" - android:layout_gravity="fill_horizontal" - android:gravity="end" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <LinearLayout - style="?android:attr/buttonBarStyle" - android:orientation="vertical" - android:layout_gravity="end"> - - <Button android:id="@+id/emergencyCallButton" - style="?android:attr/buttonBarButtonStyle" - android:layout_gravity="end" - android:layout_width="wrap_content" - android:layout_height="0dip" - android:layout_weight="1" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:text="@string/lockscreen_emergency_call" - android:drawableLeft="@drawable/lockscreen_emergency_button" - android:drawablePadding="0dip" - /> - - <Button android:id="@+id/forgotPatternButton" - style="?android:attr/buttonBarButtonStyle" - android:layout_gravity="end" - android:layout_width="wrap_content" - android:layout_height="0dip" - android:layout_weight="1" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:text="@string/lockscreen_forgot_pattern_button_text" - android:drawableLeft="@drawable/lockscreen_forgot_password_button" - android:drawablePadding="0dip" - /> - </LinearLayout> - - <!-- Column 1: lock pattern --> - - <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginTop="8dip" - android:layout_marginEnd="8dip" - android:layout_marginBottom="8dip" - android:layout_marginStart="8dip" - android:layout_rowSpan="7"/> - - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="5" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - /> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_row="0" - android:layout_column="1" - android:layout_rowSpan="7" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_marginStart="8dip" - android:layout_width="0dip" - android:layout_height="0dip" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - -</GridLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml deleted file mode 100644 index de94451..0000000 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ /dev/null @@ -1,205 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the screen that shows the 9 circle unlock widget and instructs - the user how to unlock their device, or make an emergency call. This - is the portrait layout. --> -<GridLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center_horizontal"> - - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="@dimen/keyguard_lockscreen_status_line_clockfont_top_margin" - android:layout_marginBottom="@dimen/keyguard_lockscreen_status_line_clockfont_bottom_margin" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin" - android:layout_gravity="end"> - - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@+id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/lockscreen_clock_background" - /> - - <TextView android:id="@+id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/lockscreen_clock_foreground" - /> - - </com.android.internal.widget.DigitalClock> - - <LinearLayout - android:orientation="horizontal" - android:layout_gravity="end" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin"> - - <TextView - android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - /> - - <TextView - android:id="@+id/alarm_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="16dip" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - </LinearLayout> - - - <TextView - android:id="@+id/status1" - android:layout_gravity="end" - android:layout_marginEnd="@dimen/keyguard_lockscreen_status_line_font_right_margin" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - <Space android:layout_gravity="fill" /> - - <!-- We need MATCH_PARENT here only to force the size of the parent to be passed to - the pattern view for it to compute its size. This is an unusual case, caused by - LockPatternView's requirement to maintain a square aspect ratio based on the width - of the screen. --> - <com.android.internal.widget.LockPatternView - android:id="@+id/lockPattern" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginEnd="8dip" - android:layout_marginBottom="4dip" - android:layout_marginStart="8dip" - android:layout_gravity="center_horizontal" - /> - - <TextView - android:id="@+id/carrier" - android:layout_gravity="center_horizontal" - android:singleLine="true" - android:ellipsize="marquee" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - - <!-- Footer: an emergency call button and an initially hidden "Forgot pattern" button --> - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - style="?android:attr/buttonBarStyle" - android:gravity="center" - android:weightSum="2"> - - <Button android:id="@+id/emergencyCallButton" - android:layout_gravity="center_horizontal" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - style="?android:attr/buttonBarButtonStyle" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:text="@string/lockscreen_emergency_call" - android:drawableLeft="@drawable/lockscreen_emergency_button" - android:drawablePadding="0dip" - /> - - <Button android:id="@+id/forgotPatternButton" - android:layout_gravity="center_horizontal" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - style="?android:attr/buttonBarButtonStyle" - android:textSize="@dimen/keyguard_lockscreen_status_line_font_size" - android:text="@string/lockscreen_forgot_pattern_button_text" - android:drawableLeft="@drawable/lockscreen_forgot_password_button" - android:drawablePadding="0dip" - /> - - </LinearLayout> - - <!-- Music transport control --> - <include android:id="@+id/transport" - layout="@layout/keyguard_transport_control" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="4" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_width="0dip" - android:layout_height="0dip" - /> - - <!-- Area to overlay FaceLock --> - <RelativeLayout - android:id="@+id/face_unlock_area_view" - android:visibility="invisible" - android:layout_row="4" - android:layout_column="0" - android:layout_rowSpan="1" - android:layout_columnSpan="1" - android:layout_gravity="fill" - android:layout_marginBottom="4dip" - android:layout_width="0dip" - android:layout_height="0dip" - android:background="@drawable/intro_bg"> - - <View - android:id="@+id/spotlightMask" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/facelock_spotlight_mask" - /> - - <ImageView - android:id="@+id/cancel_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_facial_backup" - /> - - </RelativeLayout> - -</GridLayout> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 09f752f..4cb7888 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Stoor"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Gebruik die USB-berging."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Lees die SD-kaart."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Toeganklikheidseienskappe"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Kenmerke wat bystandtegnologie kan versoek."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"deaktiveer of verander statusbalk"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"statusbalk"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"stuur SMS-boodskappe"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Laat die program toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige programme kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"stuur sms-boodskappe met geen bestiging"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Laat die program toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige programme kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"stuur antwoord-via-boodskap-geleenthede"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Laat die program toe om versoeke te stuur na ander boodskapprogramme om antwoord-via-boodskap-geleenthede vir inkomende oproepe te hanteer."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lees jou teksboodskappe (SMS of MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Laat die program toe om SMS-boodskappe wat op jou tablet of SIM-kaart gestoor is, te lees. Dit laat die program toe om alle SMS-boodskappe te lees, ongeag van die inhoud of vertroulikheid daarvan."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Laat die program toe om SMS-boodskappe wat op jou foon of SIM-kaart gestoor is, te lees. Dit laat die program toe om alle SMS-boodskappe te lees, ongeag van die inhoud of vertroulikheid daarvan."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Plaas die aktiwiteitbestuurder in \'n afsluitingstatus. Doen nie \'n volledige afsluiting nie."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"verhoed program-oorskakelings"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Verhoed dat die gebruiker na \'n ander program oorskakel."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"kry huidige program-inligting"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Laat die houer toe om private inligting oor die huidige program op die voorgrond van die skerm te herwin."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitor en beheer alle programlaaiery"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Laat die program toe om te monitor en te beheer hoe die stelsel aktiwiteite laai. Kwaadwillige programme kan dalk die stelsel heeltemal in gevaar stel. Hierdie toestemming is net nodig vir ontwikkeling, en nooit vir normale gebruik nie."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"stuur uitsending met pakket verwyder"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Laat \'n program toe om die huidige lae-vlak batteryverbruikdata te lees. Kan die program toelaat om gedetailleerde inligting te vind oor watter programme jy gebruik."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"verander batterystatistieke"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Laat die program toe om versamelde battery-statistieke te verander. Nie vir gebruik deur normale programme nie."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"haal programwerking-statistieke op"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Laat die program toe om versamelde programwerking-statistieke te herwin. Nie vir gebruik deur normale programme nie."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"verander programwerking-statistieke"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Laat die program toe om versamelde programwerking-statistieke te verander. Nie vir gebruik deur normale programme nie."</string> <string name="permlab_backup" msgid="470013022865453920">"beheerstelsel-rugsteun en -teruglaai"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Laat die program toe om die stelsel se rugsteun-en-teruglaai-meganisme te beheer. Nie vir gebruik deur normale programme nie."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"bevestig \'n volledige rugsteun- of teruglaaihandeling"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n invoermetode te bind. Dit moet nooit vir normale programme nodig wees nie."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"verbind aan \'n toeganklikheidsdiens"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Dit laat die houer toe om aan die top-koppelvlak van \'n toeganklikheidsdiens te verbind. Behoort nooit vir gewone programme nodig te wees nie."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"versoek verken-met-aanraking"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Laat die houer toe om \'n interaksiemodus te versoek waarin geraakte items hardop uitgespreek word en die UI via handgebare verken kan word."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"versoek verbeterde webtoeganklikheid"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Laat die houer toe om te versoek dat webtoeganklikheidsverbeterings geaktiveer word. Byvoorbeeld, om skripte te installeer om programinhoud meer toeganklik te maak."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"bind aan \'n teksdiens"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n teksdiens (bv SpellCheckerService) te bind. Dit moet nooit vir normale programme nodig wees nie."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"bind aan \'n VPN-diens"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Laat die program toe om netwerkbeleide te bestuur en program-spesifieke reëls te definieer."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verander verrekening van netwerkgebruik"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Laat die program toe om te verander hoe netwerkgebruik teenoor programme gemeet word. Nie vir gebruik deur normale programme nie."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"kry toegang tot kennisgewings"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Stel wagwoordreëls"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Beheer lengte en watter karakters wat in die skermontsluit-wagwoorde gebruik word."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitor pogings om skerm te ontsluit"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Patroon uitgevee"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Sel bygevoeg"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Patroon klaar"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Legstuk %2$d van %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Voeg legstuk by."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Leeg"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Ontsluitruimte uitgevou."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 643f959..54b5f6f 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"ማከማቻ"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"የUSB ማከማቻ ድረስ።"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SD ካርድ ድረስ"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"የተደራሽነት ባህሪያት"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"አጋዥ ቴክኖሎጂ ሊጠይቃቸው የሚችላቸው ባህሪያት።"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"የሁኔቴ አሞሌ አቦዝን ወይም ቀይር"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"የስርዓት አዶዎችን ወደ ሁኔታ አሞሌ ላለማስቻል ወይም ለማከል እና ለማስወገድ ለመተግበሪያው ይፈቅዳሉ፡፡"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"ኹናቴ አሞሌ"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"መሣሪያህ የህዋስ ስርጭት መልዕክቶች ሲቀበል መተግበሪያው እንዲያነበው ይፈቅድለታል። የህዋስ ስርጭት ማንቂያዎች አስቸኳይ ሁኔታዎች ሲያጋጥሙ አንዳንድ አካባቢዎች ላይ የሚላኩ ናቸው። የህዋስ ስርጭት ሲደርስ ተንኮል አዘል መተግበሪያዎች በመሣሪያህ አፈጻጸም ወይም አሰራር ላይ ጣልቃ ሊገቡ ይችላሉ።"</string> <string name="permlab_sendSms" msgid="5600830612147671529">"የSMS መልዕክቶች ላክ"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"መተግበሪያው የኤስ.ኤም.ኤስ. መልዕክቶችን እንዲልክ ይፈቅድለታል። ይህ ያልተጠበቁ ወጪዎችን ሊያስከትል ይችላል። ተንኮል አዘል መተግበሪያዎች ያላንተ ማረጋገጫ መልዕክቶችን በመላክ ገንዘብ ሊያስወጡህ ይችላሉ።"</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"ያለ ምንም ማረጋገጫ የSMS መልዕክቶች ላክ"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"መተግበሪያው የኤስ.ኤም.ኤስ. መልዕክቶችን እንዲልክ ይፈቅድለታል። ይህ ያልተጠበቁ ወጪዎችን ሊያስከትል ይችላል። ተንኮል አዘል መተግበሪያዎች ያላንተ ማረጋገጫ መልዕክቶችን በመላክ ገንዘብ ሊያስወጡህ ይችላሉ።"</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"የበመልዕክት-በኩል-ምላሽ-ስጥ ክስተቶችን ይላኩ"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"መተግበሪያው ሌሎች የመልዕክት መላኪያ መተግበሪያዎች ለመጪ ጥሪዎች በመልዕክት-በኩል-ምላሽ-መስጠት ስራን እንዲይዙ ጥያቄዎች እንዲልክላቸው ያስችለዋል።"</string> <string name="permlab_readSms" msgid="8745086572213270480">"የጽሑፍ መልዕክቶችህን አንብብ (ኤስ.ኤም.ኤስ. ወይም ኤም.ኤም.ኤስ.)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"መገለጫው በጡባዊ ቱኮህ ወይም በSIM ካርድህ የተከማቹ የኤስ.ኤም.ኤስ. መልእክቶችን እንዲያነብ ይፈቅድለታል። ይህ መተግበሪያው ይዘት ወይም ሚስጥራዊነትን ከግምት ሳያስገባ ሁሉንም የኤስ.ኤም.ኤስ. መልእክቶች እንዲያነብ ይፈቅድለታል።"</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"መገለጫው በስልክህ ወይም በSIM ካርድህ የተከማቹ የኤስ.ኤም.ኤስ. መልእክቶችን እንዲያነብ ይፈቅድለታል። ይህ መተግበሪያው ይዘት ወይም ሚስጥራዊነትን ከግምት ሳያስገባ ሁሉንም የኤስ.ኤም.ኤስ. መልእክቶች እንዲያነብ ይፈቅድለታል።"</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"የእንቅስቃሴውን አደራጅ ወደ ዝጋ ሁነታ አስቀምጥ።ሙሉ ለሙሉ ዝጋ አያከናውንም።"</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"የትግበራ መቀያየርን ተከላከል"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"ተጠቃሚው ከሌላ መተግበሪያ ከመቀየር ይከላከላል።"</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"የአሁኑ የመተግበሪያ መረጃ ያግኙ"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"ያዢው በማያ ገጹ ፊት ላይ ስላለው የአሁኑ መተግበሪያ የግል መረጃ እንዲያመጣ ያስችለዋል።"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"ሁሉንም መተግበሪያ ማስነሻ አሳይ እና ተቆጣጠር"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"እንቅስቃሴዎችን ስርዓቱ እንዴት እንደሚያስጀምር ለመከታተል እና ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች የስርዓቱን ክብረ ገመና ሙሉለሙሉ ሊያጋልጡ ይችላሉ፡፡ ይህ ፍቃድ የሚያስፈልገው ለግንባታ ብቻ ነው፤ ለመደበኛ አጠቃቀም ፈጽሞ አይደለም፡፡"</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"አካታች የተወገደለት ስርጭት ላክ"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"አንድ መተግበሪያ የአሁኑን የዝቅተኛ-ደረጃ ባትሪ አጠቃቀም ውሂብን እንዲያነብ ያስችላል። መተግበሪያው ስለሚጠቀሟቸው መተግበሪያዎች ዝርዝር መረጃ እንዲያገኝ ሊያስችለው ይችላል።"</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"የባትሪ ስታስቲክስን ይቀይራል"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"የተሰበሰቡ የባትሪ ስታስቲክሶችን እንዲቀይር ለመተግበሪያው ያስችለዋል። ለመደበኛ መተግበሪያዎች ጥቅም አይደለም።"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"የመተግበሪያ ክወናዎች ስታቲስቲክስን ሰርስረህ አውጣ"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"መተግበሪያው የተሰበሰቡ የክወና ስታስቲክሶችን ሰርስሮ እንዲያወጣ ይፈቅድለታል። ለመደበኛ መተግበሪያዎች ጥቅም ያልሆነ።"</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"የመተግበሪያ ክወናዎች ስታቲስቲክስን ይቀይሩ"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"መተግበሪያው የተሰበሰቡ የክወና ስታስቲክሶችን እንዲቀይር ይፈቅድሎታል። ለመደበኛ መተግበሪያዎች ጥቅም ያልሆነ።"</string> <string name="permlab_backup" msgid="470013022865453920">"የስርዓት መጠባበቂያን ተቆጣጠር እናእነበረበት መልስ"</string> <string name="permdesc_backup" msgid="6912230525140589891">"የስርዓቱን ምትኬ እና እንደነበር መልስ መንገዶችን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ በመደበኛ መተግበሪያዎች ለመጠቀም አይሆንም፡፡"</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"የሙሉ መጠበቂያ ወይም እነበረበት መልስ ከዋኝ አረጋግጥ"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ያዡ ግቤት ስልቱን ወደ ከፍተኛ-ደረጃ በይነገጽ ለመጠረዝ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ከአንድ የተደራሽነት አገልግሎት ጋር እሰር"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ያዢው ወደ የአንድ ተደራሽነት አገልግሎት ከፍተኛ-ደረጃ በይነገጽ እንዲያስር ይፈቅድለታል። ለመደበኛ መተግበሪያዎች መቼም ቢሆን ሊያስፈልግ አይገባም።"</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"በንክኪ ማሰስን ይጠይቁ"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"ያዢው የተነኩ ንጥሎች ጮክ ተብለው የሚነገሩበት እና በይነገጹ በምልክቶች በኩል ሊታሰስ በሚችሉበት የበይነግንኙነት ሁኒታ እንዲጠይቅ ያስችለዋል።"</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"የተሻሻለ የድር ተደራሽነት ይጠይቁ"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"ያዢው የድር ተደራሽነት ማሻሻያዎች እንዲነቁለት እንዲጠይቅ ያስችለዋል። ለምሳሌ፣ የመተግበሪያ ይዘት ይበልጥ ተደራሽ እንዲሆን የGoogle ስክሪፕቶችን እንዲጫኑ መጠየቅ።"</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"ለፅሁፍ አገልግሎት አሰረ"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"ያዡ ግቤት ለከፍተኛ-ደረጃ የፅሁፍ አገልግሎት ገፅታ ለመጠረዝ ይፈቅዳል። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"ለVPN አገልግሎት ተገዛ"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"የአውታረመረብ ቋሚ መመሪያዎችን እና ትግበራ ተኮር ደንቦችን ለማደራጀት ለመተግበሪያው ይፈቅዳሉ፡፡"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"የአውታረ መረብ አጠቃቀም"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"ከመተግበሪያዎች በተለየ መልኩ እንዴት የአውታረ መረብ አጠቃቀም እንደተመዘገበ ለመቀየር ለመተግበሪያው ይፈቅዳሉ።ለመደበኛ መተግበሪያዎች አገልግሎት አይውልም።"</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"ማሳወቂያዎችን ይድረሱ"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"መተግበሪያው ማሳወቂያዎችን እንዲያስመጣ፣ እንዲመረምር እና እንዲያጸዳ ያስችለዋል፣ በሌሎች መተግበሪያዎች የተለጠፉትንም ጨምሮ።"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"የይለፍ ቃል ድንቦች አዘጋጅ"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"በማያ-መክፈት የተፈቀዱ የይለፍ ቃል ርዝመት እና ቁምፊዎች ተቆጣጠር።"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"የማሳያ-ክፈት ሙከራዎችን አሳይ"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"ንድፍ ጸድቷል"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"ሕዋስ ታክሏል"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"ንድፍ ተጠናቋል"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s። ምግብር %2$d ከ%3$d።"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ንዑስ ፕሮግራም አክል"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"ባዶ"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"የመክፈቻ አካባቢ ተስፋፍቷል።"</string> @@ -940,7 +955,7 @@ </plurals> <plurals name="in_num_hours"> <item quantity="one" msgid="7164353342477769999">"በ 1 ሰዓት"</item> - <item quantity="other" msgid="547290677353727389">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item> + <item quantity="other" msgid="547290677353727389">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item> </plurals> <plurals name="in_num_days"> <item quantity="one" msgid="5413088743009839518">"ነገ"</item> @@ -972,7 +987,7 @@ </plurals> <plurals name="abbrev_in_num_hours"> <item quantity="one" msgid="3274708118124045246">"በ 1 ሰዓት"</item> - <item quantity="other" msgid="3705373766798013406">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item> + <item quantity="other" msgid="3705373766798013406">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item> </plurals> <plurals name="abbrev_in_num_days"> <item quantity="one" msgid="2178576254385739855">"ነገ"</item> @@ -1003,7 +1018,7 @@ </plurals> <plurals name="duration_hours"> <item quantity="one" msgid="8917467491248809972">"1 ሰዓት"</item> - <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item> </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"የቪዲዮ ችግር"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"ይቅርታ፣ ይህ ቪዲዮ በዚህ መሣሪያ ለመልቀቅ ትክክል አይደለም።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index c41e706..a9ac95d 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"التخزين"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"الدخول إلى وحدة تخزين USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"الدخول إلى بطاقة SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"ميزات إمكانية الوصول"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"الميزات التي يمكن للتقنية المساعدة طلبها"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"تعطيل شريط الحالة أو تعديله"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"للسماح للتطبيق بتعطيل شريط الحالة أو إضافة رموز نظام وإزالتها."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"شريط الحالة"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"السماح للتطبيق بقراءة رسائل بث الخلية التي يتلقاها هذا الجهاز. يتم تسليم تنبيهات بث الخلية في بعض المواقع لتحذيرك من حالات طارئة. يمكن أن تتداخل التطبيقات الضارة مع أداء أو تشغيل الجهاز عندما يتم تلقي بث خلية طارئ."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"إرسال رسائل قصيرة SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"للسماح للتطبيق بإرسال رسائل قصيرة SMS. وقد يؤدي هذا إلى تحمل رسوم غير متوقعة. وقد تكلفك التطبيقات الضارة أموالاً من خلال إرسال رسائل بدون موافقة منك."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"إرسال رسائل قصيرة SMS بدون تأكيد"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"للسماح للتطبيق بإرسال رسائل قصيرة SMS. وقد يؤدي هذا إلى تحمل رسوم غير متوقعة. وقد تكلفك التطبيقات الضارة أموالاً من خلال إرسال رسائل بدون موافقة منك."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"إرسال أحداث يتم الرد عليها عبر رسالة"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"السماح للتطبيق بإرسال طلبات إلى تطبيقات المراسلة الأخرى للتعامل مع الأحداث التي يتم الرد عليها عبر الرسائل في المكالمات الواردة."</string> <string name="permlab_readSms" msgid="8745086572213270480">"قراءة الرسائل النصية (الرسائل القصيرة SMS أو رسائل الوسائط المتعددة)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"للسماح للتطبيق بقراءة الرسائل القصيرة SMS المخزنة على الجهاز اللوحي أو على بطاقة SIM. ويتيح هذا للتطبيق قراءة جميع الرسائل القصيرة SMS، بغض النظر عن المحتوى أو مدى السرية."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"للسماح للتطبيق بقراءة الرسائل القصيرة SMS المخزنة على هاتفك أو على بطاقة SIM. ويتيح هذا للتطبيق قراءة جميع الرسائل القصيرة SMS، بغض النظر عن المحتوى أو مدى السرية."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"لوضع مدير الأنشطة في حالة إيقاف التشغيل. لا يتم تنفيذ إيقاف تشغيل كامل."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"منع التبديل بين التطبيقات"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"لمنع المستخدم من التبديل إلى تطبيق آخر."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"الحصول على معلومات عن التطبيق الحالي"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"للسماح للمالك باسترداد معلومات خاصة عن التطبيق الحالي في مقدمة الشاشة."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"مراقبة بدء تشغيل جميع التطبيقات والتحكم فيها"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"للسماح للتطبيق بمراقبة كيفية بدء النظام للأنشطة والتحكم فيها. قد تُعرِّض التطبيقات الضارة النظام للضرر بشكل كامل. لن تكون هناك حاجة لهذا الإذن سوى للتطوير فقط، وليس للاستخدام العادي على الإطلاق."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"إرسال بث الحزمة الذي تمت إزالته"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"للسماح لتطبيق بقراءة البيانات الحالية التي تستهلك مستوى منخفضًا من البطارية. قد يتيح التطبيق معرفة معلومات تفصيلية عن التطبيقات التي تستخدمها."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"تعديل إحصاءات البطارية"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"للسماح للتطبيق بتعديل إحصاءات البطارية المجمّعة. ليس للاستخدام بواسطة التطبيقات العادية."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"استرداد إحصاءات عمليات التطبيق"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"للسماح للتطبيق باسترداد إحصاءات عمليات التطبيق المجمّعة. ليس للاستخدام بواسطة التطبيقات العادية."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"تعديل إحصاءات تشغيل التطبيق"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"للسماح للتطبيق بتعديل إحصاءات تشغيل التطبيق المجمّعة. ليس للاستخدام بواسطة التطبيقات العادية."</string> <string name="permlab_backup" msgid="470013022865453920">"التحكم في النسخة الاحتياطية للنظام واستعادتها"</string> <string name="permdesc_backup" msgid="6912230525140589891">"للسماح للتطبيق بالتحكم في النسخة الاحتياطية للنظام وآلية الاستعادة. ليس للاستخدام بواسطة التطبيقات العادية."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"تأكيد إجراء عملية نسخ احتياطي أو استرداد كاملة"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لأسلوب الإدخال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"الالتزام بخدمة إمكانية الدخول"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة إمكانية الدخول. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"طلب الاستكشاف باللمس"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"للسماح للمالك بطلب وضع التفاعل الذي يتم فيه النطق بالعناصر الملموسة بصوت عالٍ واستكشاف واجهة المستخدم بالإيماءات."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"طلب إمكانية وصول محسنة"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"للسماح للمالك بطلب تمكين تحسينات إمكانية الوصول إلى الويب، مثل تثبيت البرامج النصية لمزيد من تسهيل الدخول إلى محتوى التطبيق."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"الالتزام بخدمة إدخال النصوص"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة إدخال النصوص (على سبيل المثال، SpellCheckerService). لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"الالتزام بخدمة VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"للسماح لتطبيق بإدارة سياسات الشبكة وتحديد قواعد متعلقة بالتطبيق."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"تعديل حساب استخدام الشبكة"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"للسماح للتطبيق بتعديل كيفية حساب استخدام الشبكة في التطبيقات. ليس للاستخدام بواسطة التطبيقات العادية."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"إشعارات الدخول"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"يتيح للتطبيق استرجاع الإشعارات وفحصها ومسحها، بما في ذلك تلك التي نشرتها تطبيقات أخرى."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"تعيين قواعد كلمة المرور"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"يمكنك التحكم في الطول والأحرف المسموح بها في كلمات مرور إلغاء تأمين الشاشة."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"مراقبة محاولات إلغاء قفل الشاشة"</string> @@ -777,7 +791,7 @@ <string name="lockscreen_transport_pause_description" msgid="7659088786780128001">"زر الإيقاف المؤقت"</string> <string name="lockscreen_transport_play_description" msgid="5888422938351019426">"زر التشغيل"</string> <string name="lockscreen_transport_stop_description" msgid="4562318378766987601">"زر الإيقاف"</string> - <string name="emergency_calls_only" msgid="6733978304386365407">"مكالمات الطوارئ فقط"</string> + <string name="emergency_calls_only" msgid="6733978304386365407">"مكالمات طوارئ فقط"</string> <string name="lockscreen_network_locked_message" msgid="143389224986028501">"الشبكة مؤمّنة"</string> <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"بطاقة SIM مؤمّنة بكود PUK."</string> <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"راجع دليل المستخدم أو اتصل بخدمة العملاء."</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"تم محو النمط"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"تمت إضافة الخلية"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"اكتمل النمط"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. الأداة %2$d من %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"إضافة أداة."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"فارغة"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"تم توسيع منطقة إلغاء القفل."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 6d03273..c1cb9d5 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Сховішча"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Атрымаць доступ да USB-назапашвальнiка."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Доступ да SD-карты."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Спецыяльныя магчымасці"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Функцыі, якія могуць запытаць дадатковыя тэхналогii."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"адключаць ці змяняць радок стану"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Дазваляе прыкладанням адключаць радок стану або дадаваць і выдаляць сістэмныя значкі."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"радок стану"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Дазваляе прыкладанню чытаць паведамленні базавай станцыі, атрыманыя прыладай. Папярэджанні базавай станцыі дасылаюцца ў некаторыя месцы, каб папярэдзіць вас аб надзвычайных сітуацыях. Шкоднасныя прыкладанні могуць уплываць на прадукцыйнасць ці працу прылады пры атрыманні паведамлення базавай станцыі аб надзвычайнай сітуацыі."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"адпраўляць SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Дазваляе прыкладанне для адпраўкі SMS паведамленняў. Гэта можа прывесці да нечаканых абвінавачванняў. Шкоднасныя праграмы могуць каштаваць вам грошай на адпраўку паведамленняў без вашага пацвярджэння."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"адпраўляць SMS-паведамленні без пацверджання"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Дазваляе прыкладанню адпраўляць SMS-паведамленнi. Гэта можа прывесці да нечаканых абвінавачванняў. Шкоднасныя прыкладаннi могуць каштаваць вам грошай на адпраўку паведамленняў без вашага пацвярджэння."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"адправiць мерапрыемства \"адказ праз паведамленне\""</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Прыкладанне зможа адпраўляць запыты ў іншыя прыкладанні для апрацоўкі паведамленняў пра мерапрыемствы туы \"рэагаваць-праз-паведамленне\" для ўваходных выклікаў."</string> <string name="permlab_readSms" msgid="8745086572213270480">"чытанне сваiх тэкставых паведамленняў (SMS ці MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Дазваляе прыкладанню чытаць SMS-паведамленнi, якія захоўваюцца на планшэце ці SIM-карце. Гэта дазваляе прыкладанням чытаць ўсе SMS-паведамленні, незалежна ад змесцiва або прыватнасці."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Дазваляе прыкладанню чытаць SMS-паведамленні, якія захоўваюцца ў тэлефоне або на SIM-карце. Гэта дазваляе прыкладанням чытаць ўсе SMS-паведамленні, незалежна ад змесцiва або прыватнасці."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Спыняе дзейнасць менеджэра. Не выконвае поўнае адключэнне."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"прадухіляць пераключэнне прыкладанняў"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Не дазваляе карыстальніку пераходзіць да іншага прыкладання."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"атрымаць бягучую інфармацыю прыкладання"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Дазваляе ўладальніку атрымлiваць асабістую інфармацыю аб бягучым прыкладаннi на пярэднім плане экрана."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"адсочваць і кантраляваць запуск усіх прыкладанняў"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Дазваляе прыкладанню сачыць і кантраляваць, як сістэма запускае працэсы. Шкоднасныя прыкладанні могуць цалкам парушыць працу сістэмы. Гэты дазвол патрэбны толькі для распрацоўкі, ніколі для звычайнага выкарыстання."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"трансляваць паведамленні аб выдаленні пакетаў"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Прыкладанне можа счытваць бягучыя звесткi пры нiзкi зарад акумулятара. Прыкладанне можа знайсцi падрабязную iнфармацы. пра прыкладаннi, якiя вы выкарыстоўваеце."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"змяняць статыстыку батарэі"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Дазваляе прыкладанням змяняць сабраную статыстыку батарэi. Не патрабуецца для звычайных прыкладанняў."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"атрымлiваць статыстыку выкарыстання прыкладання"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Дазваляе прыкладанню атрымлiваць сабраную статыстыку выкарыстання прыкладання. Не для выкарыстання звычайнымі прыкладаннямі."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"змяняць статыстыку выкарыстання прыкладання"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Дазваляе прыкладанню змяняць сабраную статыстыку выкарыстання прыкладання. Не для выкарыстання звычайнымі прыкладаннямі."</string> <string name="permlab_backup" msgid="470013022865453920">"кантраляваць рэзервовае капіяванне і аднаўленне сістэмы"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Дазваляе прыкладанням кантраляваць рэзервовае капіяванне сістэмы і механізм аднаўлення. Не патрабуецца для звычайных прыкладанняў."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"пацверджанне поўнага рэзервовага капіявання або аднаўлення"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню метада ўводу. Не патрабуецца для звычайных прыкладанняў."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"прывязацца да службы доступу"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню службы доступу. Не патрабуецца для звычайных прыкладанняў."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"запытаць даследаванне навобмацак"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Дазваляе ўладальнiку запытваць працу ў рэжыме ўзаемадзеяння, у якiм элементы, да якiх дакранулiся, прагаворваюцца ўслых, а карыстальнiцкi iнтэрфейс можна даследаваць рукамi."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"прасіць пашырэння даступнасці вэб-даступнасцi"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Дазваляе ўладальнiку запытваць уключэнне iнтэрнэт-даступнасцi паляпшэнняў. Напрыклад, усталёўка скрыптаў з Google зробiць кантэнт больш прыдатным для доступу."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"звязаць з тэкставай службай"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню тэкставай паслугі (напрыклад, SpellCheckerService). Ніколі не патрабуецца для звычайных прыкладанняў."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"звязвацца з VPN сэрвісам"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Дазваляе прыкладаннм кіраваць сеткавымі палітыкамі і вызначаць правілы пэўных прыкладанняў."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"змяніць улік выкарыстання сеткі"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дазваляе прыкладанням змяняць метад уліку выкарыстання сеткі прыкладаннямі. Не для выкарыстання звычайнымі прыкладаннямі."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"доступ да паведамленняў"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Дазваляе прыкладанню атрымлiваць, правяраць i выдаляць апавяшчэннi, у тым лiку апублiкаваныя iншымi прыкладаннямi."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Устанавіць правілы паролю"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Кіраванне даўжынёй і колькасцю знакаў у паролі разблакоўкі экрана."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Сачыць за спробамі разблакоўкі экрана"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Ключ выдалены"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Сотавы дададзены"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Ключ завершаны"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ВIджэт %2$d з %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Дадаць віджэт"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Пусты"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Вобласць разблакіроўкі разгарнута."</string> @@ -1449,7 +1464,7 @@ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> - <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдаліць"</string> + <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдалiць"</string> <string name="safe_media_volume_warning" product="default" msgid="7382971871993371648">"Павялiчыць гук больш за рэкамендаваны ўзровень?"\n"Доўгае слуханне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Утрымлiвайце два пальцы, каб уключыць доступ."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Даступнасць уключана."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index ca9311b..158134e 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Съхранение"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Достъп до USB хранилището."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Достъп до SD картата."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Функции за достъпност"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Функции, които подпомагащата технология може да заяви."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"деактивиране или промяна на лентата на състоянието"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Разрешава на приложението да деактивира лентата на състоянието или да добавя и премахва системни икони."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"лента на състоянието"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Разрешава на приложението да чете съобщения с клетъчно излъчване, получени от устройството ви. Сигналите с клетъчно излъчване се получават на някои местоположения, за да ви предупредят за спешни ситуации. Злонамерените приложения могат да възпрепятстват изпълнението или работата на устройството ви при получаване на такова спешно излъчване."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"изпращане на SMS съобщения"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Разрешава на приложението да изпраща SMS съобщения. Това може да доведе до неочаквано таксуване. Злонамерените приложения могат да ви въвлекат в разходи, като изпращат съобщения без потвърждение от ваша страна."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"изпращане на SMS съобщения без потвърждаване"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Разрешава на приложението да изпраща SMS съобщения. Това може да доведе до неочаквано таксуване. Злонамерените приложения могат да ви въвлекат в разходи, като изпращат съобщения без потвърждение от ваша страна."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"изпращане на покани за отговор чрез съобщение"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Разрешава на приложението да изпраща заявки до други приложения за съобщения, за да обработва покани за отговор чрез SMS за входящите обаждания."</string> <string name="permlab_readSms" msgid="8745086572213270480">"четене на текстовите ви съобщения (SMS или MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Разрешава на приложението да чете SMS съобщенията, съхранени на таблета или SIM картата ви. Това разрешение му позволява да чете всички съобщения независимо от съдържанието или поверителността."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Разрешава на приложението да чете SMS съобщенията, съхранени на телефона или SIM картата ви. Това разрешение му позволява да чете всички съобщения независимо от съдържанието или поверителността."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Изключва диспечера на дейностите. Не извършва пълно изключване."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"предотвратяване на превключването между приложения"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Не позволява на потребителя да превключва към друго приложение."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"извличане на информация за текущото приложение"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Разрешава на собственика да извлича частна информация за текущото приложение на преден план на екрана."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"наблюдение и контрол на стартирането на всички приложения"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Разрешава на приложението да наблюдава и контролира как системата стартира дейности. Злонамерените приложения могат изцяло да компрометират системата. Това разрешение е нужно само за програмиране, никога за нормална употреба."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"изпращане на излъчване при премахнат пакет"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Разрешава на приложението да чете текущите данни за работа при ниско ниво на батерията. Може да му разреши да намери подробна информация за ползваните от вас приложения."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"промяна на статистическите данни за батерията"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Разрешава на приложението да променя събраните статистически данни за батерията. Не е предназначено за нормални приложения."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"извличане на статистическите данни за операциите на приложението"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Разрешава на приложението да извлича събраните статистически данни за операциите си. Не е предназначено за нормални приложения."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"промяна на статистическите данни за операциите на приложението"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Разрешава на приложението да променя събраните статистически данни за операциите си. Не е предназначено за нормални приложения."</string> <string name="permlab_backup" msgid="470013022865453920">"контролиране на създаването и възстановяването на резервни копия на системата"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Разрешава на приложението да контролира системния механизъм за създаване и възстановяване на резервни копия. Не е предназначено за нормални приложения."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"потвърждаване на пълно резервно копие или възстановяване на операцията"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на метод на въвеждане. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"обвързване с услуга за достъпност"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на услуга за достъпност. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"заявяване на изследване чрез докосване"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Разрешава на собственика да заяви режим на взаимодействие, при който докоснатите елементи се изговарят на глас и потребителският интерфейс може да бъде изследван чрез жестове."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"заявяване на подобрена достъпност в мрежата"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Разрешава на собственика да заяви активирането на подобрения на достъпността в мрежата – например инсталирането на скриптове, за да стане съдържанието от приложенията по-достъпно."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"обвързване с текстова услуга"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на текстова услуга (напр. SpellCheckerService). Нормалните приложения би трябвало никога да не се нуждаят от това."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"обвързване с услуга за VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Разрешава на приложението да управлява правилата на мрежата и да определя такива за конкретно приложение."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"промяна на отчетността на употребата на мрежа"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Разрешава на приложението да променя това как употребата на мрежа се отчита спрямо приложенията. Не е предназначено за нормални приложения."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"достъп до известията"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Разрешава на приложението да извлича, преглежда и изчиства известия, включително публикуваните от други приложения."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Задаване на правила за паролата"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролирайте дължината и разрешените знаци за паролите за отключване на екрана."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Наблюдаване на опитите за отключване на екрана"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Фигурата е изчистена"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Клетката е добавена"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Фигурата е завършена"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Приспособление %2$d от %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Добавяне на приспособление."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Празно"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Областта за отключване е разгъната."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 2a6a333..e3ee35d 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Emmagatzematge"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Accedeix a l\'emmag. USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Accedeix a la targeta SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funcions d\'accessibilitat"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funcions que pot sol·licitar la tecnologia d\'assistència."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra d\'estat"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barra d\'estat"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permet que l\'aplicació llegeixi missatges de difusió mòbil rebuts pel dispositiu. Les alertes de difusió mòbil s\'entreguen en algunes ubicacions per alertar de situacions d\'emergència. És possible que les aplicacions malicioses interfereixin en el rendiment o en el funcionament del dispositiu quan es rep una difusió mòbil d\'emergència."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"enviar missatges SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permet que l\'aplicació enviï missatges SMS. Aquesta acció pot provocar càrrecs inesperats. Les aplicacions malicioses poden enviar missatges sense la teva confirmació, cosa que et pot fer gastar diners."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"enviament de missatges SMS sense confirmació"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permet que l\'aplicació enviï missatges SMS. Aquesta acció pot provocar càrrecs inesperats. Les aplicacions malicioses poden enviar missatges sense la teva confirmació, cosa que et pot fer gastar diners."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"envia els esdeveniments amb \"resposta per missatge\""</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permet que l\'aplicació enviï sol·licituds a altres aplicacions de missatgeria per gestionar els esdeveniments amb \"resposta per missatge\" de les trucades entrants."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lectura dels missatges de text (SMS o MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permet que l\'aplicació llegeixi missatges SMS emmagatzemats a la tauleta o a la targeta SIM. Això permet que l\'aplicació llegeixi tots els missatges SMS, independentment del contingut o de la confidencialitat."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permet que l\'aplicació llegeixi missatges SMS emmagatzemats al telèfon o a la targeta SIM. Això permet que l\'aplicació llegeixi tots els missatges SMS, independentment del contingut o de la confidencialitat."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Posa el gestor d\'activitats en estat d\'apagada. No fa una apagada completa."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir els canvis d\'aplicació"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impedeix que l\'usuari canviï a una altra aplicació."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obtenció d\'informació de l\'aplicació actual"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permet que el titular recuperi informació privada sobre l\'aplicació actual al primer pla de la pantalla."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"supervisa i controla tots els inicis d\'aplicacions"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permet que l\'aplicació supervisi i controli com el sistema inicia activitats. Les aplicacions malicioses poden comprometre totalment el sistema. Aquest permís només és necessari per al desenvolupament, mai per a l\'ús normal."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar difusió d\'eliminació de paquet"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permet que l\'aplicació llegeixi l\'ús de dades actual quan hi ha poca bateria. Pot permetre que l\'aplicació recopili informació detallada sobre les aplicacions que fas servir."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifica les estadístiques de la bateria"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permet que l\'aplicació modifiqui les estadístiques d\'ús de la bateria recopilades. No ho poden fer servir les aplicacions normals."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"recuperació d\'estadístiques d\'ús de l\'aplicació"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permet que l\'aplicació recuperi les estadístiques d\'ús de l\'aplicació recopilades. No indicat per a les aplicacions normals."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica les estadístiques d\'ús de l\'aplicació"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permet que l\'aplicació modifiqui les estadístiques d\'ús de l\'aplicació recopilades. No indicat per a les aplicacions normals."</string> <string name="permlab_backup" msgid="470013022865453920">"controlar la còpia de seguretat i restauració del sistema"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permet que l\'aplicació controli el mecanisme de còpia de seguretat i restauració del sistema. No indicat per a les aplicacions normals."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar una operació de còpia de seguretat completa o de restauració"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permet que el titular vinculi a la interfície de nivell superior d\'un mètode d\'entrada. No s\'hauria de necessitar mai per a les aplicacions normals."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vincular amb un servei d\'accessibilitat"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permet vincular amb la interfície de nivell superior d\'un servei d\'accessibilitat. Les aplicacions normals no haurien de necessitar-ho."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"sol·licitud d\'exploració tàctil"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Permet que el titular sol·liciti un mode d\'interacció en què els elements que es toquin es llegeixin en veu alta i la interfície d\'usuari es pugui explorar mitjançant gestos."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"sol·licitud d\'accessibilitat web millorada"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Permet que el titular sol·liciti l\'activació de les millores per a l\'accessibilitat web. Per exemple, la instal·lació de scripts de Google per fer més accessible el contingut de l\'aplicació."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"vincula a un servei de text"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permet al titular vincular amb la interfície de nivell superior d\'un servei de text (per exemple, SpellCheckerService). Les aplicacions normals mai no ho haurien de necessitar."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"vincula a un servei de VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permet que l\'aplicació gestioni les polítiques de la xarxa i que defineixi les regles específiques d\'aplicació."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificació del càlcul d\'ús de la xarxa"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permet que l\'aplicació modifiqui la manera com es calcula l\'ús de la xarxa per part de les aplicacions. No indicat per a les aplicacions normals."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"accedeix a les notificacions"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet que l\'aplicació recuperi, examini i esborri les notificacions, incloses les que han publicat altres aplicacions."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Defineix les normes de contrasenya"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controla la longitud i els caràcters permesos a les contrasenyes de desbloqueig de pantalla."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Control d\'intents de desbloqueig de pantalla"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Patró esborrat"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"S\'ha afegit una cel·la"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Patró completat"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Afegeix un widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Buit"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"S\'ha ampliat l\'àrea de desbloqueig."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index f361a42..35798a4 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Úložiště"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Přístup do úložiště USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Přístup ke kartě SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funkce usnadnění"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkce, o které může asistenční technologie požádat."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"zakázání či změny stavového řádku"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikaci zakázat stavový řádek nebo přidat či odebrat systémové ikony."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"stavový řádek"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Umožňuje aplikaci číst zprávy informační služby přijaté ve vašem zařízení. Upozornění informační služby jsou v některých oblastech odesílána za účelem varování před mimořádnými událostmi. Škodlivé aplikace mohou narušit výkon či provoz vašeho zařízení během přijímání zpráv informační služby."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"odesílaní zpráv SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Umožňuje aplikaci odesílat zprávy SMS. Může to mít za následek účtování neočekávaných poplatků. Škodlivé aplikace vás mohou připravit o peníze odesíláním zpráv bez vašeho svolení."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"posílat zprávy SMS bez potvrzení"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Umožňuje aplikaci odesílat zprávy SMS. Může to mít za následek účtování neočekávaných poplatků. Škodlivé aplikace vás mohou připravit o peníze odesíláním zpráv bez vašeho svolení."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"odesílání událostí typu „odpovězte zprávou“"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Umožňuje aplikaci odesílat ostatním aplikacím požadavky na zpracování událostí typu „odpovězte zprávou“ pro příchozí volání."</string> <string name="permlab_readSms" msgid="8745086572213270480">"čtení textových zpráv (SMS nebo MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Umožňuje aplikaci číst zprávy SMS uložené v tabletu nebo na SIM kartě.Toto oprávnění umožňuje aplikaci číst zprávy SMS bez ohledu na jejich obsah nebo důvěrnost."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Umožňuje aplikaci číst zprávy SMS uložené v telefonu nebo na SIM kartě. Toto oprávnění umožňuje aplikaci číst zprávy SMS bez ohledu na jejich obsah nebo důvěrnost."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Uvede správce činností do vypnutého stavu. Nedojde však k úplnému vypnutí."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zabránění přepínání aplikací"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Zabrání uživateli přepnout na jinou aplikaci."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"získat informace o aktuální aplikaci"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Umožňuje držiteli získat soukromé informace o aktuální aplikaci na popředí obrazovky."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"sledování a řízení spouštění všech aplikací"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Umožňuje aplikaci sledovat a řídit spouštění činností systémem. Škodlivé aplikace mohou systém zcela ovládnout. Toto oprávnění je požadováno pouze pro účely vývoje, nikdy pro běžné použití."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"odeslání vysílání o odstranění balíčku"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Umožňuje aplikaci číst aktuální podrobné údaje o využití baterie. Aplikace to může využít k získání podrobných informací o tom, které aplikace používáte."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"změna statistických údajů o baterii"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Umožňuje aplikaci upravit shromážděné statistiky o baterii. Toto oprávnění není určeno pro běžné aplikace."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"načtení statistik operací aplikace"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Umožňuje aplikaci načíst shromážděné statistiky operací aplikace. Toto oprávnění není určeno pro běžné aplikace."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"upravit statistiky operací aplikace"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Umožňuje aplikaci upravit shromážděné statistiky operací aplikace. Toto oprávnění není určeno pro běžné aplikace."</string> <string name="permlab_backup" msgid="470013022865453920">"ovládání zálohování a obnovy systému"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Umožňuje aplikaci řídit mechanismy zálohování a obnovení systému. Toto oprávnění není určeno pro běžné aplikace."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potvrzení operace úplné zálohy nebo úplného obnovení"</string> @@ -325,8 +333,8 @@ <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Umožňuje aplikaci využívat jiné aplikace nebo části uživatelského rozhraní. Aplikace tak může zasahovat do používání rozhraní jakékoli aplikace a měnit rozhraní zobrazené v jiných aplikacích."</string> <string name="permlab_setAnimationScale" msgid="2805103241153907174">"změna globální rychlosti animace"</string> <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Umožňuje aplikaci kdykoliv globálně změnit rychlost animací (rychlejší či pomalejší animace)."</string> - <string name="permlab_manageAppTokens" msgid="1286505717050121370">"správa klíčů aplikací"</string> - <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"Umožňuje aplikaci vytvořit a spravovat vlastní klíče a současně obejít pořadí vykreslování. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> + <string name="permlab_manageAppTokens" msgid="1286505717050121370">"správa aplikačních tokenů"</string> + <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"Umožňuje aplikaci vytvořit a spravovat vlastní tokeny a současně obejít pořadí vykreslování. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> <string name="permlab_freezeScreen" msgid="4708181184441880175">"zmrazit obrazovku"</string> <string name="permdesc_freezeScreen" msgid="8558923789222670064">"Povoluje aplikaci dočasně zmrazit obrazovku pro přechod do režimu celé obrazovky."</string> <string name="permlab_injectEvents" msgid="1378746584023586600">"používání kláves a tlačítek"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Umožňuje držiteli vázat se na nejvyšší úroveň rozhraní pro zadávání dat. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"navázat se na službu usnadnění přístupu"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Umožňuje držiteli navázat se na nejvyšší úroveň rozhraní služby usnadnění přístupu. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"požádat o prozkoumání dotykem"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Umožňuje držiteli požádat o interaktivní režim, ve kterém jsou po dotyku vysloveny názvy položek a uživatelské rozhraní lze poznávat pomocí gest."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"požádat o usnadnění přístupu k webu"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Umožňuje držiteli požádat o aktivaci usnadnění přístupu k webu, například o instalaci skriptů, které usnadňují přístup k obsahu aplikace."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"navázat se na textovou službu"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Umožňuje držiteli připojit se k nejvyšší úrovni rozhraní textové služby (např. SpellCheckerService). Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"navázat se na službu VPN"</string> @@ -406,19 +418,19 @@ <string name="permlab_writeContacts" msgid="5107492086416793544">"úprava kontaktů"</string> <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Umožňuje aplikaci upravit údaje o kontaktech uložených v tabletu včetně toho, jak často voláte, posíláte e-maily nebo komunikujete jinými způsoby s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech."</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Umožňuje aplikaci upravit údaje o kontaktech uložených v telefonu včetně toho, jak často voláte, posíláte e-maily nebo komunikujete jinými způsoby s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech."</string> - <string name="permlab_readCallLog" msgid="3478133184624102739">"číst seznam hovorů"</string> + <string name="permlab_readCallLog" msgid="3478133184624102739">"čtení seznamu hovorů"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Umožňuje aplikaci číst seznam hovorů v tabletu, včetně dat o příchozích a odchozích hovorech. Toto oprávnění umožňuje aplikaci ukládat údaje ze seznamu hovorů. Škodlivé aplikace mohou tyto údaje bez vašeho vědomí sdílet."</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Umožňuje aplikaci číst seznam hovorů v telefonu, včetně dat o příchozích a odchozích hovorech. Toto oprávnění umožňuje aplikaci ukládat údaje ze seznamu hovorů. Škodlivé aplikace mohou tyto údaje bez vašeho vědomí sdílet."</string> - <string name="permlab_writeCallLog" msgid="8552045664743499354">"zapisovat seznam hovorů"</string> + <string name="permlab_writeCallLog" msgid="8552045664743499354">"zápis do seznamu hovorů"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Umožňuje aplikaci upravovat seznam hovorů vašeho tabletu, včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Umožňuje aplikaci upravovat seznam hovorů vašeho telefonu, včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string> <string name="permlab_readProfile" msgid="4701889852612716678">"čtení vaší vlastní vizitky"</string> <string name="permdesc_readProfile" product="default" msgid="5462475151849888848">"Umožňuje aplikaci číst údaje v osobním profilu uložené v zařízení, například jméno nebo kontaktní údaje. Znamená to, že vás aplikace může identifikovat a odeslat údaje z profilu dalším aplikacím."</string> <string name="permlab_writeProfile" msgid="907793628777397643">"úprava vaší vlastní vizitky"</string> <string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Umožňuje aplikaci změnit nebo přidat údaje osobního profilu uložené v zařízení, například jméno nebo kontaktní údaje. Znamená to, že vás aplikace může identifikovat a odeslat údaje z profilu dalším aplikacím."</string> - <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"číst váš sociální stream"</string> + <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"čtení vašeho sociálního streamu"</string> <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Umožňuje aplikaci získat přístup k sociálním aktualizacím od vašich přátel a synchronizaci těchto aktualizací. Při sdílení informací buďte opatrní – toto oprávnění umožňuje aplikaci číst komunikaci mezi vámi a vašimi přáteli v sociálních sítích bez ohledu na její důvěrnost. Poznámka: Toto oprávnění nemusí platit pro všechny sociální sítě."</string> - <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"zapisovat do vašeho sociálního streamu"</string> + <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"zápis do sociálního streamu"</string> <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Umožňuje aplikaci zobrazit sociální aktualizace od vašich přátel. Při sdílení informací buďte opatrní – aplikace s tímto oprávněním může vytvářet zprávy, které zdánlivě pochází od vašich přátel. Poznámka: Toto oprávnění nemusí platit pro všechny sociální sítě."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"čtení událostí kalendáře a důvěrné informace"</string> <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Umožňuje aplikaci číst všechny události kalendáře uložené v tabletu, včetně událostí přátel nebo spolupracovníků. Aplikace s tímto oprávněním může sdílet nebo ukládat údaje v kalendáři bez ohledu na důvěrnost nebo citlivost těchto údajů."</string> @@ -444,7 +456,7 @@ <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Povoluje aplikaci připojit a konfigurovat displeje přes Wi-Fi."</string> <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ovládat displeje přes Wi-Fi"</string> <string name="permdesc_controlWifiDisplay" msgid="4543912292681826986">"Povoluje aplikaci ovládat základní funkce displejů přes Wi-Fi."</string> - <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"změna vašeho nastavení zvuku"</string> + <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"změna nastavení zvuku"</string> <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikaci změnit globální nastavení zvuku, například hlasitost či reproduktor pro výstup zvuku."</string> <string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávání zvuku"</string> <string name="permdesc_recordAudio" msgid="4906839301087980680">"Umožňuje aplikaci zaznamenat zvuk pomocí mikrofonu. Toto oprávnění umožňuje aplikaci kdykoliv zaznamenat zvuk bez vašeho svolení."</string> @@ -501,8 +513,8 @@ <string name="permdesc_modifyPhoneState" msgid="1029877529007686732">"Umožňuje aplikaci ovládat telefonní funkce zařízení. Aplikace s tímto oprávněním smí bez upozornění přepínat sítě, zapínat a vypínat bezdrátový modul telefonu a podobně."</string> <string name="permlab_readPhoneState" msgid="9178228524507610486">"čtení stavu a identity telefonu"</string> <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Umožňuje aplikaci získat přístup k telefonním funkcím zařízení. Toto oprávnění umožňuje aplikaci zjistit telefonní číslo telefonu, identifikační čísla zařízení, zda zrovna probíhá hovor, a vzdálené číslo, ke kterému je hovor připojen."</string> - <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"zabránění přechodu tabletu do režimu spánku"</string> - <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"zabránění přechodu telefonu do režimu spánku"</string> + <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"bránění přechodu tabletu do režimu spánku"</string> + <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"bránění přechodu telefonu do režimu spánku"</string> <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Umožňuje aplikaci zabránit přechodu tabletu do režimu spánku."</string> <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Umožňuje aplikaci zabránit přechodu telefonu do režimu spánku."</string> <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"zapnutí či vypnutí tabletu"</string> @@ -526,15 +538,15 @@ <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Umožňuje aplikaci změnit časové pásmo telefonu."</string> <string name="permlab_accountManagerService" msgid="4829262349691386986">"role služby AccountManagerService"</string> <string name="permdesc_accountManagerService" msgid="1948455552333615954">"Umožňuje aplikaci volat funkce AccountAuthenticator."</string> - <string name="permlab_getAccounts" msgid="1086795467760122114">"vyhledání účtů v zařízení"</string> + <string name="permlab_getAccounts" msgid="1086795467760122114">"vyhledávání účtů v zařízení"</string> <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Umožňuje aplikaci získat seznam účtů v tabletu. Mohou sem patřit i účty vytvořené aplikacemi, které jste nainstalovali."</string> <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Umožňuje aplikaci získat seznam účtů v telefonu. Mohou sem patřit i účty vytvořené aplikacemi, které jste nainstalovali."</string> <string name="permlab_authenticateAccounts" msgid="5265908481172736933">"vytváření účtů a nastavení hesel"</string> <string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"Umožňuje aplikaci používat funkce aplikace AccountManager související s ověřováním účtů – včetně vytváření účtů a získávání a nastavování hesel."</string> - <string name="permlab_manageAccounts" msgid="4983126304757177305">"přidání nebo odebrání účtů"</string> + <string name="permlab_manageAccounts" msgid="4983126304757177305">"přidávání nebo odebírání účtů"</string> <string name="permdesc_manageAccounts" msgid="8698295625488292506">"Umožňuje aplikaci provádět operace, jako je přidávání nebo odebírání účtů nebo mazání jejich hesel."</string> <string name="permlab_useCredentials" msgid="235481396163877642">"používání účtů v zařízení"</string> - <string name="permdesc_useCredentials" msgid="7984227147403346422">"Umožňuje aplikaci požadovat ověřovací klíče."</string> + <string name="permdesc_useCredentials" msgid="7984227147403346422">"Umožňuje aplikaci požadovat ověřovací tokeny."</string> <string name="permlab_accessNetworkState" msgid="4951027964348974773">"zobrazení síťových připojení"</string> <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Umožňuje aplikaci zobrazit informace o síťových připojeních, například o tom, které sítě jsou k dispozici a které jsou připojené."</string> <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"úplný přístup k síti"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Umožňuje aplikaci spravovat zásady sítě a definovat pravidla pro konkrétní aplikace."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"upravit kontrolu používání sítě"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Umožňuje aplikaci upravit způsob výpočtu využití sítě aplikacemi. Toto oprávnění není určeno pro běžné aplikace."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"přístup k oznámením"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikacím načítat, zobrazovat a mazat oznámení včetně těch přidaných jinými aplikacemi."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavit pravidla pro heslo"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Řídit délku hesel pro odemčení obrazovky a povolené znaky."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Sledovat pokusy o odemčení obrazovky"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Bezpečnostní gesto vymazáno"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Buňka přidána"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Bezpečnostní gesto dokončeno"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d z %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Přidat widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prázdné"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Oblast odemknutí byla rozšířena."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index e974576..477f313 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Få adgang til USB-lager."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Få adgang til SD-kortet."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Tilgængelighedsfunktioner"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funktioner, som hjælpeteknologier kan anmode om."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"deaktiver eller rediger statuslinje"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"statusbjælke"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillader, at appen læser mobiltransmissionsbeskeder, der modtages af din enhed. I nogle områder sendes mobiltransmissionsbeskeder for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af din enhed, når en mobiltransmission om en nødsituation modtages."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"send sms-beskeder"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillader, at appen kan sende sms-beskeder. Dette kan resultere i uventede opkrævninger. Skadelige apps kan koste dig penge ved at sende beskeder uden din bekræftelse."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"sende sms-meddelelser uden bekræftelse"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Tillader, at appen kan sende sms-beskeder. Dette kan resultere i uventede opkrævninger. Skadelige apps kan koste dig penge ved at sende beskeder uden din bekræftelse."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send hændelser, hvor der skal svares pr. besked"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Tillader, at appen kan sende anmodninger til andre apps til beskeder for at håndtere hændelser, hvor der skal svares pr. besked."</string> <string name="permlab_readSms" msgid="8745086572213270480">"læse dine tekstbeskeder (SMS eller MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillader, at appen kan læse de sms-beskeder, der er gemt på din tablet eller dit SIM-kort. Med denne tilladelse kan appen læse alle sms-beskeder, uanset indhold eller fortrolighed."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Tillader, at appen kan læse de sms-beskeder, der er gemt på din telefon eller dit SIM-kort. Med denne tilladelse kan appen læse alle sms-beskeder, uanset indhold eller fortrolighed."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Sætter aktivitetsadministratoren i lukningstilstand. Lukker ikke helt ned."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"undgå programskift"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Forhindrer brugeren i at skifte til en anden app."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"få aktuelle app-oplysninger"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Tillader, at brugeren henter private oplysninger om den aktuelle applikation i forgrunden på skærmen."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"overvåge og kontrollere åbning af alle apps"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Tillader, at appen kan overvåge og kontrollere, hvordan systemet starter aktiviteter. Ondsindede apps kan fuldstændig kompromittere systemet. Denne tilladelse er kun nødvendig til udvikling, aldrig til normal brug."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"send udsendelse om fjernet pakke"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillader, at en applikation læser de aktuelle data for batteriforbruget. Kan tillade, at applikationen henter detaljerede oplysninger om, hvilke apps du bruger."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rediger batteristatistikker"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillader, at appen kan ændre indsamlede batteristatistikker. Anvendes ikke af normale apps."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hent statistikker for handlinger i appen"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Tillader, at appen indhenter statistikker for handlinger i applikationen. Denne handling bruges ikke i almindelige apps."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"lav ændringer i statistik for handlinger i appen"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillader, at appen kan ændre indsamlede statistikker for handlinger i applikationen. Dette kan ikke bruges af almindelige apps."</string> <string name="permlab_backup" msgid="470013022865453920">"kontroller sikkerhedskopiering af system, og gendan"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Tillader, at appen kan kontrollere systemets sikkerhedskopi og gendannelsesmekanisme. Kan ikke anvendes af normale apps."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"bekræfte en komplet sikkerhedskopi, eller gendan drift"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Tillader, at brugeren kan forpligter sig til en inputmetodes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"bind dig til en tilgængelighedstjeneste"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Tillader, at brugeren binder sig til en grænseflade for en tilgængelighedstjeneste på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"anmod om udforsk ved berøring"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Tillader brugeren at anmode om en interaktionstilstand, hvor elementer, der berøres, læses op, og brugergrænsefladen kan udforskes via berøringer."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"anmod om bedre webtilgængelighed"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Tillader, at brugeren kan anmode om aktivering af webtilgængelighedsforbedringer. Disse omfatter bl.a. at installering af scripts, der gør appindhold mere tilgængeligt."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"forpligte sig til en sms-tjeneste"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Tillader, at ejeren kan binde en teksttjenestes grænseflade (f. eks. SpellCheckerService) på øverste niveau. Dette bør aldrig være nødvendigt til normale apps."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"bind til en VPN-tjeneste"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Tillader, at appen kan administrere netværkspolitikker og definere appspecifikke regler."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"skift afregning af netværksbrug"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillader, at appen kan ændre den måde, som netværksforbrug udregnes på i forhold til apps. Anvendes ikke af normale apps."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"adgang til underretninger"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde underretninger, herunder dem, der er sendt af andre apps."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Indstil regler for adgangskode"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller længden samt tilladte tegn i adgangskoder til oplåsning af skærmen."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Mønster er ryddet"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Celle er tilføjet"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Mønster er afsluttet"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d af %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Tilføj widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tom"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Oplåsningsområdet er udvidet."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 3547321..770cc61 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Speicher"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Zugriff auf USB-Speicher"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Zugriff auf SD-Karte"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funktionen der Bedienungshilfen"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funktionen, die für Bedienungshilfentechnologie beantragt werden können"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"Statusleiste deaktivieren oder ändern"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"Statusleiste"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ermöglicht der App, von Ihrem Gerät empfangene Cell Broadcast-Nachrichten zu lesen. Cell Broadcast-Benachrichtigungen werden an einigen Standorten gesendet, um Sie über Notfallsituationen zu informieren. Schädliche Apps können die Leistung oder den Betrieb Ihres Geräts beeinträchtigen, wenn eine Cell Broadcast-Notfallbenachrichtigung eingeht."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMS senden"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Ermöglicht der App, SMS zu senden. Dies kann zu unerwarteten Kosten führen. Schädliche Apps können Kosten verursachen, indem sie Nachrichten ohne Ihre Bestätigung senden."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"SMS ohne Bestätigung senden"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Ermöglicht der App, SMS zu senden. Dies kann zu unerwarteten Kosten führen. Schädliche Apps können Kosten verursachen, indem sie Nachrichten ohne Ihre Bestätigung senden."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"Antwort-per-SMS/MMS-Ereignisse senden"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Ermöglicht der App, Anfragen an andere SMS/MMS-Apps zu senden, um Antwort-per-SMS/MMS-Ereignisse für eingehende Anrufe zu verarbeiten"</string> <string name="permlab_readSms" msgid="8745086572213270480">"SMS oder MMS lesen"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Ermöglicht der App, auf Ihrem Tablet oder Ihrer SIM-Karte gespeicherte SMS zu lesen. Die App kann alle SMS lesen, unabhängig von Inhalt und Vertraulichkeit."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Ermöglicht der App, auf Ihrem Telefon oder Ihrer SIM-Karte gespeicherte SMS zu lesen. Die App kann alle SMS lesen, unabhängig von Inhalt und Vertraulichkeit."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Versetzt den Aktivitätsmanager in einen heruntergefahrenen Zustand. Führt kein vollständiges Herunterfahren aus."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"App-Wechsel verhindern"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hindert den Nutzer daran, zu einer anderen App zu wechseln"</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"Informationen zur aktuellen App abrufen"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Ermöglicht es dem Inhaber, private Informationen zur aktuellen App im Vordergrund des Bildschirms abzurufen"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"Start von Apps überwachen und steuern"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Ermöglicht der App, den Start von Systemaktivitäten zu überwachen und zu steuern. Schädliche Apps können so das gesamte System beeinträchtigen. Diese Berechtigung wird nur zu Entwicklungszwecken und nie für die normale Nutzung benötigt."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"Broadcast ohne Paket senden"</string> @@ -315,13 +319,17 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Ermöglicht einer Anwendung, den momentan niedrigen Akkustand zu erkennen. Unter Umständen erhält die App detaillierte Informationen darüber, welche Apps Sie verwenden."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"Akkudaten ändern"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Ermöglicht der App, erfasste Akkudaten zu ändern. Nicht für normale Apps vorgesehen."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"App-Vorgangsstatistiken abrufen"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Ermöglicht der App, erfasste App-Vorgangsstatistiken abzurufen. Kann nicht von normalen Apps verwendet werden."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"App-Vorgangsstatistiken ändern"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Ermöglicht der App, erfasste App-Vorgangsstatistiken zu ändern. Kann nicht von normalen Apps verwendet werden"</string> <string name="permlab_backup" msgid="470013022865453920">"Systemsicherung und -wiederherstellung kontrollieren"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Ermöglicht der App, den Sicherungs- und Wiederherstellungsmechanismus des Systems zu steuern. Nicht für normale Apps vorgesehen."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Vollständige Sicherung oder Wiederherstellung bestätigen"</string> <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Ermöglicht der App, die Benutzeroberfläche zur Bestätigung der vollständigen Sicherung zu starten. Kann nicht von jeder App verwendet werden."</string> <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"Nicht autorisierte Fenster anzeigen"</string> <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Ermöglicht der App die Erstellung von Fenstern, die von der Benutzeroberfläche des internen Systems verwendet werden. Nicht für normale Apps vorgesehen."</string> - <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"Über andere Apps ziehen"</string> + <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"Über andere Apps zeichnen"</string> <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Ermöglicht der App, über andere Apps oder Teile der Benutzeroberfläche zu zeichnen. Dies kann sich auf die Oberfläche in jeder App auswirken oder die erwartete Darstellung in anderen Apps verändern."</string> <string name="permlab_setAnimationScale" msgid="2805103241153907174">"Allgemeine Animationsgeschwindigkeit einstellen"</string> <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Ermöglicht der App, die allgemeine Animationsgeschwindigkeit (langsamere oder schnellere Animationen) jederzeit anzupassen."</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Ermöglicht dem Halter, sich an die Oberfläche einer Eingabemethode auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"An eine Bedienungshilfe binden"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Ermöglicht dem Halter, sich an die Oberfläche einer Bedienungshilfe auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"Tippen & Entdecken beantragen"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Nutzer darf Interaktionsmodus beantragen, in dem berührte Elemente laut vorgelesen werden und die Benutzeroberfläche über Gesten erkundet werden kann."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"Erweiterte Webbedienungshilfen beantragen"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Nutzer darf Verbesserungen für Webbedienungshilfen beantragen, beispielsweise die Installation von Skripts von Google, um die Nutzerfreundlichkeit des App-Inhalts zu verbessern."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"An einen Textdienst binden"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Ermöglicht dem Halter, sich an die Oberfläche eines Textdienstes auf oberster Ebene zu binden, z. B. eines Rechtschreibprüfungsdienstes. Sollte nie für normale Apps benötigt werden."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"An einen VPN-Dienst binden"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Ermöglicht der App, Netzwerkrichtlinien zu verwalten und anwendungsspezifische Regeln festzulegen"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Zuordnung für Netzwerknutzung ändern"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ermöglicht der App, die Art und Weise zu ändern, wie der Netzwerkverbrauch im Hinblick auf Apps berechnet wird. Nicht für normale Apps vorgesehen."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"Auf Benachrichtigungen zugreifen"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Ermöglicht der App das Abrufen, Überprüfen und Löschen von Benachrichtigungen, einschließlich Benachrichtigungen, die von anderen Apps gepostet wurden"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Passwortregeln festlegen"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Zulässige Länge und Zeichen für Passwörter zum Entsperren des Bildschirms festlegen"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Versuche zum Entsperren des Displays überwachen"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Muster gelöscht"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Zelle hinzugefügt"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Muster abgeschlossen"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d von %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Widget hinzufügen"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Leer"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Entsperr-Bereich maximiert"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 70c1550..5e3c56b 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Αποθηκευτικός χώρος"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Πρόσβαση στον χώρο αποθ. USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Πρόσβαση στην κάρτα SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Λειτουργίες προσβασιμότητας"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Λειτουργίες που μπορεί να ζητήσει η τεχνολογία υποβοήθησης."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποίηση ή τροποποίηση γραμμής κατάστασης"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"γραμμή κατάστασης"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων που έχουν μεταδοθεί μέσω κινητού τηλεφώνου και έχουν ληφθεί από τη συσκευή σας. Ειδοποιήσεις που μεταδίδονται μέσω κινητού παραδίδονται σε ορισμένες τοποθεσίες για να σας προειδοποιήσουν για καταστάσεις έκτακτης ανάγκης. Κακόβουλες εφαρμογές ενδέχεται να παρεμποδίσουν την απόδοση ή τη λειτουργία της συσκευής σας κατά τη λήψη μετάδοσης μέσω κινητού σχετικά με μια επείγουσα κατάσταση."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"αποστολή μηνυμάτων SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Επιτρέπει στην εφαρμογή των αποστολή μηνυμάτων SMS. Αυτό μπορεί να προκαλέσει μη αναμενόμενες χρεώσεις. Οι κακόβουλες εφαρμογές ενδέχεται να σας κοστίσουν χρήματα, αποστέλλοντας μηνύματα χωρίς την έγκρισή σας."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"αποστολή μηνυμάτων SMS χωρίς επιβεβαίωση"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Επιτρέπει στην εφαρμογή των αποστολή μηνυμάτων SMS. Αυτό μπορεί να προκαλέσει μη αναμενόμενες χρεώσεις. Οι κακόβουλες εφαρμογές ενδέχεται να σας κοστίσουν χρήματα, αποστέλλοντας μηνύματα χωρίς την έγκρισή σας."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"αποστολή συμβάντων απόκρισης μέσω μηνύματος"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Επιτρέπει στην εφαρμογή να στέλνει μηνύματα σε άλλες εφαρμογές ανταλλαγής μηνυμάτων για τη διαχείριση συμβάντων απόκρισης μέσω μηνύματος για εισερχόμενες κλήσεις."</string> <string name="permlab_readSms" msgid="8745086572213270480">"ανάγνωση των μηνυμάτων κειμένου σας (SMS ή MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων SMS που είναι αποθηκευμένα στο tablet σας ή στην κάρτα σας SIM. Αυτό δίνει τη δυνατότητα στην εφαρμογή να διαβάζει όλα τα μηνύματα SMS, ανεξάρτητα από το περιεχόμενο ή το επίπεδο εμπιστευτικότητάς τους."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων SMS που είναι αποθηκευμένα στο τηλέφωνό σας ή στην κάρτα σας SIM. Αυτό δίνει τη δυνατότητα στην εφαρμογή να διαβάζει όλα τα μηνύματα SMS, ανεξάρτητα από το περιεχόμενο ή το επίπεδο εμπιστευτικότητάς τους."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Θέτει το πρόγραμμα διαχείρισης δραστηριοτήτων σε κατάσταση τερματισμού λειτουργιών. Δεν εκτελεί πλήρη τερματισμό λειτουργιών."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"αποτροπή εναλλαγών εφαρμογών"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Δεν επιτρέπει στο χρήστη να μεταβεί σε άλλη εφαρμογή."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"λήψη πληροφοριών σχετικά με την τρέχουσα εφαρμογή"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Δίνει στον κάτοχο τη δυνατότητα ανάκτησης απόρρητων πληροφοριών σχετικά με την τρέχουσα εφαρμογή στο προσκήνιο της οθόνης."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"παρακολούθηση και έλεγχος όλων των εκκινήσεων εφαρμογών"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Επιτρέπει στην εφαρμογή να παρακολουθεί και να ελέγχει τον τρόπο με τον οποίο το σύστημα εκκινεί δραστηριότητες. Τυχόν κακόβουλες εφαρμογές ενδέχεται να θέσουν σε κίνδυνο το σύστημα. Αυτή η άδεια είναι απαραίτητη μόνο για σκοπούς ανάπτυξης και ποτέ για συνήθη χρήση."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"αποστολή εκπομπής χωρίς πακέτο"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Επιτρέπει σε μια εφαρμογή να διαβάζει τα δεδομένα τρέχουσας χαμηλού επιπέδου χρήσης μπαταρίας. Ενδέχεται να επιτρέπει στην εφαρμογή να εντοπίσει λεπτομερείς πληροφορίες σχετικά με τις εφαρμογές που χρησιμοποιείτε."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"τροποποίηση στατιστικών στοιχείων μπαταρίας"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Επιτρέπει στην εφαρμογή την τροποποίηση στατιστικών στοιχείων μπαταρίας που έχουν συλλεχθεί. Δεν πρέπει να χρησιμοποιείται από συνήθεις εφαρμογές."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"ανάκτηση στατιστικών στοιχείων λειτουργίας για εφαρμογές"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Επιτρέπει στην εφαρμογή την ανάκτηση των στατιστικών στοιχείων λειτουργίας για τις εφαρμογές που έχουν συλλεχθεί. Δεν προορίζεται για χρήση από κανονικές εφαρμογές."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"τροποποίηση στατιστικών στοιχείων λειτουργιών εφαρμογών"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Επιτρέπει στην εφαρμογή την τροποποίηση των στατιστικών στοιχείων λειτουργίας εφαρμογών που έχουν συλλεχθεί. Δεν προορίζεται για χρήση από κανονικές εφαρμογές."</string> <string name="permlab_backup" msgid="470013022865453920">"αντίγραφο ασφαλείας και επαναφορά συστήματος"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Επιτρέπει στην εφαρμογή τον έλεγχο του μηχανισμού δημιουργίας αντιγράφων ασφάλειας και ανάκτησης. Δεν προορίζεται για χρήση με συνήθεις εφαρμογές."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"επιβεβαίωση δημιουργίας πλήρους αντιγράφου ασφαλείας ή επαναφοράς λειτουργίας"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας μεθόδου εισόδου. Δεν απαιτείται για συνήθεις εφαρμογές."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"δέσμευση σε υπηρεσία προσβασιμότητας"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανώτατου επιπέδου μιας υπηρεσίας προσβασιμότητας. Δεν απαιτείται σε κανονικές εφαρμογές."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"αίτημα εξερεύνησης με αφή"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Επιτρέπει στον κάτοχο να ζητήσει μια λειτουργία αλληλεπίδρασης κατά την οποία τα στοιχεία που αγγίζει ο χρήστης εκφωνούνται και είναι δυνατή η εξερεύνηση της διεπαφής χρήστη μέσω αφής."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"αίτημα βελτιωμένης προσβασιμότητας στον ιστό"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Επιτρέπει στον κάτοχο να ζητήσει την ενεργοποίηση βελτιώσεων προσβασιμότητας ιστού. Για παράδειγμα, εγκατάσταση σεναρίων από την Google προκειμένου το περιεχόμενο των εφαρμογών να είναι πιο προσβάσιμο."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"δέσμευση σε υπηρεσία ανταλλαγής μηνυμάτων"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Επιτρέπει στον κάτοχο τη σύνδεση με τη διεπαφή ανωτέρου επιπέδου μιας υπηρεσίας ανταλλαγής μηνυμάτων (π.χ. SpellCheckerService). Δεν είναι απαραίτητο για κανονικές εφαρμογές."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"δέσμευση σε υπηρεσία VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Επιτρέπει στην εφαρμογή τη διαχείριση των πολιτικών δικτύου και τον ορισμό κανόνων για ορισμένες εφαρμογές."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"τροποποίηση υπολογισμού χρήσης δικτύου"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Επιτρέπει στην εφαρμογή την τροποποίηση του τρόπου υπολογισμού της χρήσης δικτύου έναντι των εφαρμογών. Δεν προορίζεται για χρήση από συνήθεις εφαρμογές."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"πρόσβαση στις ειδοποιήσεις"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Επιτρέπει στην εφαρμογή να ανακτά, να εξετάζει και να απαλείφει ειδοποιήσεις, συμπεριλαμβανομένων εκείνων που δημοσιεύονται από άλλες εφαρμογές."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Ορισμός κανόνων κωδικού πρόσβασης"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Έλεγχος του μεγέθους και των χαρακτήρων που επιτρέπονται στους κωδικούς πρόσβασης ξεκλειδώματος οθόνης."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Το μοτίβο απαλείφθηκε"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Προστέθηκε κελί"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Το μοτίβο ολοκληρώθηκε"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Γραφικό στοιχείο %2$d από %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Προσθήκη γραφικού στοιχείου"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Κενή"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Ανάπτυξη της περιοχής ξεκλειδώματος."</string> @@ -1276,7 +1291,7 @@ <item quantity="one" msgid="8167147081136579439">"1 αποτέλεσμα"</item> <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> από <xliff:g id="TOTAL">%d</xliff:g>"</item> </plurals> - <string name="action_mode_done" msgid="7217581640461922289">"Ολοκληρώθηκε"</string> + <string name="action_mode_done" msgid="7217581640461922289">"Τέλος"</string> <string name="progress_unmounting" product="nosdcard" msgid="3923810448507612746">"Αποσύνδεση του χώρου αποθήκευσης USB..."</string> <string name="progress_unmounting" product="default" msgid="1327894998409537190">"Αφαίρεση κάρτας SD..."</string> <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Διαγραφή χώρου αποθήκευσης USB..."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index c1c78e2..4af1886 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Storage"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Access the USB storage."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Access the SD card."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Accessibility features"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Features that assistive technology can request."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"status bar"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"send SMS messages"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Allows the app to send SMS messages. This may result in unexpected charges. Malicious apps may cost you money by sending messages without your confirmation."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"send SMS messages with no confirmation"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Allows the app to send SMS messages. This may result in unexpected charges. Malicious apps may cost you money by sending messages without your confirmation."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send respond-via-message events"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Allows the app to send requests to other messaging apps to handle respond-via-message events for incoming calls."</string> <string name="permlab_readSms" msgid="8745086572213270480">"read your text messages (SMS or MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Allows the app to read SMS messages stored on your tablet or SIM card. This allows the app to read all SMS messages, regardless of content or confidentiality."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Allows the app to read SMS messages stored on your phone or SIM card. This allows the app to read all SMS messages, regardless of content or confidentiality."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Puts the activity manager into a shut-down state. Does not perform a complete shut down."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"prevent app switches"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Prevents the user from switching to another app."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"get current app info"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Allows the holder to retrieve private information about the current application in the foreground of the screen."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitor and control all app launching"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Allows the app to monitor and control how the system launches activities. Malicious apps may completely compromise the system. This permission is only needed for development, never for normal use."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"send package removed broadcast"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Allows an application to read the current low-level battery use data. May allow the application to find out detailed information about which apps you use."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modify battery statistics"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Allows the app to modify collected battery statistics. Not for use by normal apps."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"retrieve app ops statistics"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Allows the app to retrieve collected application operation statistics. Not for use by normal apps."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modify app ops statistics"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Allows the app to modify collected component usage statistics. Not for use by normal apps."</string> <string name="permlab_backup" msgid="470013022865453920">"control system back up and restore"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Allows the app to control the system\'s backup and restore mechanism. Not for use by normal apps."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirm a full backup or restore operation"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Allows the holder to bind to the top-level interface of an input method. Should never be needed for normal apps."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"bind to an accessibility service"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Allows the holder to bind to the top-level interface of an accessibility service. Should never be needed for normal apps."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"request explore by touch"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Allows the hoder to request an interaction mode in which touched items are spoken aloud and the UI can be explored via gestures."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"request enhanced web accessibility"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Allows the hoder to request enabling of web accessibility enhancements. For example, installing scripts to make app content more accessible."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"bind to a text service"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Allows the holder to bind to the top-level interface of a text service (e.g. SpellCheckerService). Should never be needed for normal applications."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"bind to a VPN service"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Allows the app to manage network policies and define app-specific rules."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modify network usage accounting"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Allows the app to modify how network usage is accounted against apps. Not for use by normal apps."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"access notifications"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Allows the app to retrieve, examine, and clear notifications, including those posted by other apps."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Set password rules"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Control the length and the characters allowed in screen-unlock passwords."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitor screen-unlock attempts"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Pattern cleared"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Cell added"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Pattern completed"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d of %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Add widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Empty"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Unlock area expanded."</string> @@ -1094,8 +1109,7 @@ <string name="volume_icon_description_notification" msgid="7044986546477282274">"Notification volume"</string> <string name="ringtone_default" msgid="3789758980357696936">"Default ringtone"</string> <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Default ringtone (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> - <!-- no translation found for ringtone_silent (7937634392408977062) --> - <skip /> + <string name="ringtone_silent" msgid="7937634392408977062">"None"</string> <string name="ringtone_picker_title" msgid="3515143939175119094">"Ringtones"</string> <string name="ringtone_unknown" msgid="5477919988701784788">"Unknown ringtone"</string> <plurals name="wifi_available"> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index cf1c9db..8517662 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Espacio de almacenamiento"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Acceder al almacenamiento USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Acceder a la tarjeta SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funciones de accesibilidad"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funciones que la tecnología de asistencia puede solicitar."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra de estado"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite a la aplicación leer los mensajes de difusión móvil que recibe tu dispositivo. En algunas ubicaciones, las alertas de difusión móvil se envían para informar situaciones de emergencia. Las aplicaciones maliciosas pueden afectar el rendimiento o funcionamiento de tu dispositivo cuando se recibe un un mensaje de difusión móvil de emergencia."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"enviar mensajes SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que la aplicación envíe mensajes SMS, lo que puede generar cargos inesperados. Las aplicaciones malintencionadas pueden causarte gastos imprevistos al enviar mensajes sin tu confirmación."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"Enviar mensajes SMS sin confirmación"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permite que la aplicación envíe mensajes SMS, lo que puede generar cargos inesperados. Las aplicaciones malintencionadas pueden causarte gastos imprevistos al enviar mensajes sin tu confirmación."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"enviar eventos de respuesta por mensaje"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permite que la aplicación envíe solicitudes a otras aplicaciones de mensajería para administrar eventos de respuesta por mensaje para las llamadas entrantes."</string> <string name="permlab_readSms" msgid="8745086572213270480">"leer tus mensajes de texto (SMS o MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que la aplicación consulte los mensajes SMS almacenados en la tableta o en la tarjeta SIM. La aplicación puede utilizar este permiso para leer todos los mensajes SMS, independientemente de cuál sea su contenido o su nivel de confidencialidad."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permite que la aplicación consulte los mensajes SMS almacenados en el dispositivo o en la tarjeta SIM. La aplicación puede utilizar este permiso para leer todos los mensajes SMS, independientemente de cuál sea su contenido o su nivel de confidencialidad."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Pone al administrador de la actividad en estado de cierre. No realiza un cierre completo."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir conmutadores de aplicación"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Evita que el usuario cambie a otra aplicación."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obtener información de aplicación actual"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite que el titular recupere información privada sobre la aplicación actual en el primer plano de la pantalla."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"supervisar y controlar la ejecución de todas las aplicaciones"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que la aplicación supervise y controle la manera en la que el sistema inicia actividades. Las aplicaciones maliciosas pueden comprometer el sistema por completo. Este permiso es necesario solo para el desarrollo, nunca para el uso habitual."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar emisión de paquete eliminado"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite a una aplicación leer los datos actuales de uso de batería de bajo nivel. Puede permitir a la aplicación buscar información detallada sobre las aplicaciones que usas."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar las estadísticas de la batería"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite a la aplicación modificar las estadísticas recopiladas de la batería. Las aplicaciones normales no deben utilizarlo."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"Recuperar estadísticas de operaciones de la aplicación"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permite que la aplicación recupere las estadísticas recopiladas de operación de la aplicación. Las aplicaciones normales no deben utilizar este permiso."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estadísticas de uso de aplicaciones"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas sobre el uso de aplicaciones. Las aplicaciones normales no deben utilizar este permiso."</string> <string name="permlab_backup" msgid="470013022865453920">"copia de seguridad y restauración del sistema de control"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permite que la aplicación controle el mecanismo de copia de seguridad y restauración del sistema. Las aplicaciones normales no deben utilizar este permiso."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Confirmar una copia completa de seguridad o una operación de restauración"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite al propietario vincularse a la interfaz de nivel superior de un método de entrada. Las aplicaciones normales no deberían necesitar este permiso."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vincular a un servicio de accesibilidad"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite al propietario vincularse a la interfaz de nivel superior de un servicio de accesibilidad. Las aplicaciones normales no deberían necesitar este permiso."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"solicitar exploración táctil"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Le permite al titular solicitar un modo de interacción en el cual los elementos tocados se pronuncian en voz alta y la IU se puede explorar a través de gestos."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"solicitar accesibilidad mejorada de la Web"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Le permite al titular solicitar la habilitación de las mejoras en la accesibilidad de la Web. Por ejemplo, instalar las secuencias de comandos de Google para hacer el contenido de la aplicación más accesible."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"vincular a un servicio de texto"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite al titular vincularse a la interfaz de nivel superior de un servicio de texto (p. ej., SpellCheckerService). Las aplicaciones normales no deberían necesitar este permiso."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"vincular con un servicio de VPN"</string> @@ -546,7 +558,7 @@ <string name="permlab_changeTetherState" msgid="5952584964373017960">"cambiar la conectividad de anclaje a red"</string> <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Permite que la aplicación cambie el estado de la conectividad de anclaje a la red."</string> <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiar la configuración del uso de datos del fondo"</string> - <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"Permite que la aplicación cambe la configuración de uso de los datos de referencia."</string> + <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"Permite que la aplicación cambe la configuración de uso de los datos en segundo plano."</string> <string name="permlab_accessWifiState" msgid="5202012949247040011">"ver conexiones Wi-Fi"</string> <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Permite que la aplicación vea información sobre la conexión a redes Wi-Fi, por ejemplo, si la conexión Wi-Fi está activada y cuál es el nombre de los dispositivos Wi-Fi conectados."</string> <string name="permlab_changeWifiState" msgid="6550641188749128035">"conectarse y desconectarse de la red Wi-Fi"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que la aplicación administre las políticas de red y defina reglas específicas de la aplicación."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Modificar la administración del uso de redes"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y elimine notificaciones, incluidas aquellas publicadas por otras aplicaciones."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Establecer reglas de contraseña"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas para desbloquear la pantalla"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Supervisa los intentos para desbloquear la pantalla"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Se eliminó el patrón"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Se agregó una celda."</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Se completó el patrón"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Agregar widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vacío"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área desbloqueada expandida."</string> @@ -888,7 +903,7 @@ <string name="permlab_updateLock" msgid="3527558366616680889">"no realizar actualizaciones automáticas"</string> <string name="permdesc_updateLock" msgid="1655625832166778492">"Permite a su propietario ofrecer información al sistema acerca de cuándo sería adecuado reiniciar el sistema de forma no interactiva y actualizar el dispositivo."</string> <string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string> - <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no."</string> + <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no"</string> <string name="save_password_remember" msgid="6491879678996749466">"Recuerda"</string> <string name="save_password_never" msgid="8274330296785855105">"Nunca"</string> <string name="open_permission_deny" msgid="7374036708316629800">"No tienes permiso para abrir esta página."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 824b42a..2b657c8 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Almacenamiento"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Acceso a almacenamiento USB"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Acceder a la tarjeta SD"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funciones de accesibilidad"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funciones que pueden solicitar tecnología adaptada"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"inhabilitar o modificar la barra de estado"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que la aplicación lea mensajes de difusión móvil que haya recibido el dispositivo. Las alertas de difusión móvil se envían en algunas ubicaciones para avisar de situaciones de emergencia. Es posible que las aplicaciones malintencionadas interfieran en el rendimiento o en el funcionamiento del dispositivo si se recibe una alerta de difusión móvil de emergencia."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"enviar mensajes SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que la aplicación envíe mensajes SMS, lo que puede generar cargos inesperados. Las aplicaciones malintencionadas pueden causarte gastos imprevistos al enviar mensajes sin tu confirmación."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"enviar mensajes SMS sin confirmación"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permite que la aplicación envíe mensajes SMS, lo que puede generar cargos inesperados. Las aplicaciones malintencionadas pueden causarte gastos imprevistos al enviar mensajes sin tu confirmación."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"enviar eventos de respuesta mediante mensaje"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permite que la aplicación envíe solicitudes a otras aplicaciones de mensajería para procesar eventos de respuesta mediante mensaje para llamadas entrantes."</string> <string name="permlab_readSms" msgid="8745086572213270480">"leer tus mensajes de texto (SMS o MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que la aplicación lea mensajes SMS almacenados en el tablet o en la tarjeta SIM. La aplicación puede utilizar este permiso para leer todos los mensajes SMS, independientemente de cuál sea su contenido o su nivel de confidencialidad."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permite que la aplicación lea mensajes SMS almacenados en el teléfono o en la tarjeta SIM. La aplicación puede utilizar este permiso para leer todos los mensajes SMS, independientemente de cuál sea su contenido o su nivel de confidencialidad."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Pone el administrador de actividades en estado de cierre. No realiza un cierre completo."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"evitar cambios de aplicación"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Evita que el usuario cambie a otra aplicación."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obtener información de la aplicación actual"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite que el titular recupere información privada sobre la aplicación actual en el primer plano de la pantalla."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"supervisar y controlar la ejecución de todas las aplicaciones"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que la aplicación supervise y controle la ejecución de las actividades del sistema. Las aplicaciones malintencionadas pueden vulnerar la seguridad del sistema. Este permiso es necesario únicamente para tareas de desarrollo, nunca para el uso habitual."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar emisión eliminada de paquete"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite que una aplicación consulte los datos actuales de uso de batería de nivel inferior. Puede permitir que la aplicación obtenga información detallada sobre las aplicaciones que utilizas."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar estadísticas de la batería"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite que la aplicación modifique las estadísticas recopiladas sobre la batería. Las aplicaciones normales no deben usar este permiso."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"recuperar estadísticas de operaciones de aplicaciones"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permite que la aplicación recupere las estadísticas recopiladas sobre operaciones de aplicaciones. Las aplicaciones normales no deben usar este permiso."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estadísticas de operaciones de aplicaciones"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas sobre operaciones de aplicaciones. Las aplicaciones normales no deben usar este permiso."</string> <string name="permlab_backup" msgid="470013022865453920">"controlar las copias de seguridad y las restauraciones del sistema"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permite que la aplicación controle el mecanismo de copia de seguridad y restauración del sistema. Las aplicaciones normales no deben usar este permiso."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar restauración o copia de seguridad completa"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite que se enlace con la interfaz de nivel superior de un método de entrada de texto. Las aplicaciones normales no deberían necesitar este permiso."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"enlazar con un servicio de accesibilidad"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite enlazar con la interfaz de nivel superior de un servicio de accesibilidad. Las aplicaciones normales no deberían necesitar este permiso."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"Solicitar exploración por tacto"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Permite al titular solicitar un modo de interacción en el que los elementos que se toquen se reproduzcan en voz alta y la interfaz de usuario se pueda explorar mediante gestos."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"Solicitar accesibilidad web mejorada"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Permite que el titular solicite que se habiliten mejoras de accesibilidad web. Por ejemplo, instalar secuencias de comandos de Google para que el contenido de la aplicación sea más accesible."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"enlazar con un servicio de texto"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite enlazar con la interfaz de nivel superior de un servicio de texto (por ejemplo, SpellCheckerService). Las aplicaciones normales no deberían necesitar este permiso."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"enlazar con un servicio VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que la aplicación administre políticas de red y defina reglas específicas de la aplicación."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificar cálculo de uso de red"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y borre notificaciones, incluidas las que han publicado otras aplicaciones."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Establecimiento de reglas de contraseña"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas de bloqueo de pantalla"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Control de intentos de bloqueo de pantalla"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Patrón borrado"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Se ha añadido una celda."</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Patrón completado"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Añadir widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vacío"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área de desbloqueo ampliada"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 6e7b87c..3303f00 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Mäluruum"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Juurdepääs USB-mäluseadmele."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Juurdepääs SD-kaardile."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Pääsufunktsioonid"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funktsioonid, mida saab taotleda abistav tehnoloogia."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"keela või muuda olekuriba"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"olekuriba"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Võimaldab rakendusel lugeda seadme vastu võetud mobiilsidesõnumeid. Mobiilsidemärguandeid edastatakse mõnes asukohas eriolukorrast teavitamiseks. Pahatahtlikud rakendused võivad segada seadme toimivust või tööd eriolukorra sõnumi vastuvõtmisel."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"saada SMS-sõnumeid"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Võimaldab rakendusel saata SMS-sõnumeid. See võib kaasa tuua ootamatuid tasusid. Pahatahtlikud rakendused võivad teile tekitada kulusid, saates sõnumeid teie kinnituseta."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"lühisõnumite saatmine ilma kinnituseta"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Võimaldab rakendusel saata SMS-sõnumeid. See võib kaasa tuua ootamatuid tasusid. Pahatahtlikud rakendused võivad teile tekitada kulusid, saates sõnumeid teie kinnituseta."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"sõnumiga vastamise sündmuste saatmine"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Lubab rakendusel saata taotlusi teistele sõnumiside rakendustele, et käsitleda sissetulevate kõnedele sõnumiga vastamise sündmusi."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lugege oma tekstisõnumeid (SMS või MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Võimaldab rakendusel lugeda tahvelarvutisse või SIM-kaardile salvestatud SMS-sõnumeid. See võimaldab rakendusel lugeda kõiki SMS-sõnumeid sisust või konfidentsiaalsusest hoolimata."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Võimaldab rakendusel lugeda telefoni või SIM-kaardile salvestatud SMS-sõnumeid. See võimaldab rakendusel lugeda kõiki SMS-sõnumeid sisust või konfidentsiaalsusest hoolimata."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Lülitab tegevushalduri väljalülitusolekusse. Ei lülita lõplikult välja."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"väldi rakenduste ümberlülitamist"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Takistab kasutaja lülitumist teisele rakendusele."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"aktiivse rakenduse teabe hankimine"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Lubab õiguste saajal hankida privaatset teavet ekraanil esiplaanil oleva aktiivse rakenduse kohta."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"Kõigi rakenduste käivitumise jälgimine ja juhtimine"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Võimaldab rakendusel jälgida ja juhtida, kuidas süsteem tegevusi käivitab. Pahatahtlikud rakendused võivad süsteemi täielikult rikkuda. Seda õigust on vaja ainult arenduseks, mitte tavakasutuse korral."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"saada paketist eemaldatud saade"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Lubab rakendusel lugeda madala akutaseme kasutusandmeid. Võib lubada rakendusel hankida üksikasjalikku teavet selle kohta, mis rakendusi te kasutate."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"aku statistika muutmine"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Võimaldab rakendusel muuta aku kohta kogutud statistikat. Mitte kasutada tavarakenduste puhul."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"rakenduse tööstatistika hankimine"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Võimaldab rakendusel hankida kogutud rakenduse tööstatistikat. Mitte kasutada tavarakenduste puhul."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"rakenduse tööstatistika muutmine"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Võimaldab rakendusel muuta kogutud rakenduse tööstatistikat. Mitte kasutada tavarakenduste puhul."</string> <string name="permlab_backup" msgid="470013022865453920">"juhi süsteemi varundust ja taastet"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Võimaldab rakendusel juhtida süsteemi varundus- ja taastemehhanismi. Mitte kasutada tavarakenduste puhul."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"täieliku varukoopia kinnitamine või toimingu taastamine"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lubab omanikul siduda sisestusmeetodi ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sidumine juurdepääsuteenusega"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lubab omanikul luua sideme juurdepääsuteenuse ülataseme liidesega. Tavarakenduste puhul ei tohiks seda kunagi vaja minna."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"puudutusega uurimise taotlus"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Võimaldab taotleda suhtlusrežiimi, milles puudutatud üksused valjusti välja öeldakse ja kus kasutajaliidest saab kasutada liigutuste abil."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"täiustatud veebijuurdepääsu taotlus"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Võimaldab taotleda veebijuurdepääsu täiustuste lubamist. Näiteks installida skripte, mis hõlbustavad juurdepääsu rakenduse sisule."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"tekstiteenusega sidumine"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Võimaldab omanikul siduda tekstiteenuse (nt SpellCheckerService) ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"seo VPN-teenusega"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Võimaldab rakendusel hallata võrgueeskirju ja määratleda rakendusespetsiifilisi reegleid."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"võrgukasutuse arvestamise muutmine"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Võimaldab rakendusel muuta võrgukasutuse loendamist rakenduste suhtes. Mitte kasutada tavarakenduste puhul."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"juurdepääsu märguanded"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Võimaldab rakendusel tuua, kontrollida ja kustutada märguandeid, sh neid, mille on postitanud teised rakendused."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Parooli reeglite määramine"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrollige ekraaniluku avamise paroolide pikkust ja tähemärke."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Ekraani avamiskatsed"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Muster on kustutatud"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Lahter on lisatud"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Muster on valmis"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Vidin %2$d/%3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Vidina lisamine."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tühi"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Avamisala on laiendatud."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 51f1967..b4a15c5 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"حافظه"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"به حافظهٔ USB دسترسی پیدا کنید."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"به کارت SD دسترسی داشته باشید."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"ویژگیهای قابلیت دسترسی"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"ویژگیهایی که فناوری مفید میتواند درخواست کند."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"غیرفعال کردن یا تغییر نوار وضعیت"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"به برنامه اجازه میدهد تا نوار وضعیت را غیرفعال کند یا نمادهای سیستم را اضافه یا حذف کند."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"نوار وضعیت"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"به برنامه اجازه میدهد پیامهای پخش سلولی دستگاه شما را بخواند. هشدارهای پخش سلولی در برخی از موقعیتهای مکانی تحویل داده میشوند تا موقعیتهای اضطراری را به شما اعلام کنند. وقتی پخش سلولی دریافت میشود، ممکن است برنامههای مخرب در عملکرد یا کارکرد دستگاه شما اختلال ایجاد کنند."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"ارسال پیامک ها"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"به برنامه اجازه میدهد پیامکها را ارسال کند. این باعث ایجاد هزینههای پیشبینی نشده میشود. برنامههای مخرب ممکن است با ارسال پیام بدون تأیید شما هزینههایی را برای شما ایجاد کنند."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"ارسال پیامک بدون تأیید"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"به برنامه اجازه میدهد پیامکها را ارسال کند. این باعث ایجاد هزینههای پیشبینی نشده میشود. برنامههای مخرب ممکن است با ارسال پیام بدون تأیید شما هزینههایی را برای شما ایجاد کنند."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"ارسال رویدادهای «پاسخ از طریق پیام»"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"به برنامه اجازه میدهد درخواستها را برای دیگر برنامههای پیامرسانی بفرستد تا به رویدادهای «پاسخ از طریق پیام» برای تماسهای دریافتی رسیدگی کند."</string> <string name="permlab_readSms" msgid="8745086572213270480">"خواندن پیامهای نوشتاری شما (پیامک یا MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"به برنامه اجازه میدهد پیامکهای ذخیره شده در رایانهٔ لوحی یا سیم کارت شما را بخواند. این ویژگی به برنامه امکان میدهد همه پیامکها را صرفنظر از محتوا یا محرمانه بودن آنها بخواند."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"به برنامه اجازه میدهد پیامکهای ذخیره شده در تلفن یا سیم کارت شما را بخواند. این ویژگی به برنامه امکان میدهد همه پیامکها را صرفنظر از محتوا یا محرمانه بودن آنها بخواند."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"مدیر فعالیت را در حالت خاموشی قرار میدهد. خاموشی را به صورت کامل انجام نمیدهد."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ممانعت از جابجایی برنامه"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"اجازه نمیدهد کاربر به برنامه دیگری برود."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"دریافت اطلاعات برنامه فعلی"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"به دارنده اجازه میدهد اطلاعات خصوصی مربوط به برنامه فعلی را در پیش زمینه صفحه بازیابی کند."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"نظارت و کنترل راهاندازی همه برنامه"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"به برنامه اجازه میدهد تا نحوه راهاندازی فعالیتهای سیستم را کنترل کند. برنامههای مخرب میتوانند کاملا با سیستم سازگار شوند. این مجوز فقط برای توسعه نیاز است و برای استفاده عادی نیست."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"ارسال پخش بسته حذف شده"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"به یک برنامه کاربردی اجازه میدهد که دادههای استفاده کننده از میزان باتری کم کنونی را بخواند. این کار ممکن است به برنامه این امکان را بدهد که اطلاعات جزئی درباره برنامههایی که استفاده میکنید، بدست آورد."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"اصلاح آمار باتری"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"به برنامه اجازه میدهد تا آمار جمعآوری شده باتری را تغییر دهد. برای استفاده برنامههای عادی نیست."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"بازیابی آمار مربوط به کارکرد برنامه"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"به برنامه امکان میدهد آمار جمعآوری شده مربوط به عملکرد برنامه را بازیابی کند. برای استفاده توسط برنامههای معمولی، در نظر گرفته نشده است."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"تغییر آمار کارکرد برنامه"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"به برنامه اجازه تغییر آمار کارکرد جمعآوری شده از برنامه را میدهد. برای استفاده توسط برنامههای معمولی نیست."</string> <string name="permlab_backup" msgid="470013022865453920">"کنترل نسخهٔ پشتیبان سیستم و بازیابی"</string> <string name="permdesc_backup" msgid="6912230525140589891">"به برنامه اجازه میدهد پشتیبان سیستم را کنترل کند و مکانیستم را بازیابی کند. برای استفاده برنامههای عادی نیست."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"تهیهٔ نسخهٔ پشتیبان کامل را تأیید کرده یا عملیات را بازیابی کنید"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"به دارنده این دستگاه اجازه میدهد تا به رابط سطح بالای یک روش ورودی متصل شود. این ویژگی هیچگاه برای برنامههای معمولی ضروری نمیباشد."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"اتصال به سرویس دسترسی"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"به دارنده اجازه میدهد که به رابط سطح بالای سرویس دسترسی متصل شود. هرگز برای برنامههای معمولی مورد نیاز نیست."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"درخواست کاوش با لمس"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"به دارنده اجازه میدهد یک حالت تعاملی را درخواست کند که در این حالت موارد لمس شده به صدای بلند بیان میشوند و رابط کاربری از طریق حرکات اشاره قابل کاوش است."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"درخواست افزایش دسترسی به وب"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"به دارنده اجازه میدهد فعال کردن افزایش دسترسی به وب را درخواست کند، برای مثال، نصب اسکریپتها، تا محتوای برنامه بیشتر در دسترس باشد."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"اتصال به یک سرویس متنی"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"به دارنده اجازه میدهد خود را به یک رابط سطح بالای خدمات متنی مرتبط کند (برای مثال SpellCheckerService). هرگز برای برنامههای عادی لازم نیست."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"اتصال به یک سرویس VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"به برنامه اجازه میدهد تا خط مشیهای شبکه را مدیریت کند و قوانین خاص برنامه را تعیین کند."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"اصلاح محاسبه استفاده از شبکه"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"به برنامه اجازه میدهد تا نحوه محاسبه کاربرد شبکه در برنامه را تغییر دهد. برای استفاده برنامههای عادی نیست."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"اعلانهای دسترسی"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"به برنامه اجازه میدهد به بازیابی، بررسی و پاک کردن اعلانها از جمله موارد پست شده توسط سایر برنامهها بپردازد."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"طول و نویسههای مجاز در گذرواژههای بازکردن قفل صفحه را کنترل کنید."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاشهای قفل گشایی صفحه"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"الگو پاک شد"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"سلول اضافه شد"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"الگو تکمیل شد"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ابزارک %2$d از %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ابزارک اضافه کنید."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"خالی"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"منطقه بازگشایی گسترده شد."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 8bbd645..45f4a05 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Tallennustila"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Käytä USB-tallennustilaa."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Käytä SD-korttia."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Esteettömyysominaisuudet"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Ominaisuudet, joiden käyttöönottoa avustava tekniikka voi pyytää."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"poista tilapalkki käytöstä tai muokkaa tilapalkkia"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"tilapalkki"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Antaa sovelluksen lukea laitteesi vastaanottamia tiedotteita. Tiedotteiden avulla ilmoitetaan hätätilanteista joissakin paikoissa. Haitalliset sovellukset voivat häiritä laitteen toimintaa laitteen vastaanottaessa hätätiedotteen."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"lähetä tekstiviestejä"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Antaa sovelluksen lähettää tekstiviestejä. Tästä voi aiheutua odottamattomia kuluja. Haitalliset sovellukset voivat aiheuttaa kuluja lähettämällä viestejä ilman lupaasi."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"lähettää tekstiviestejä ilman vahvistusta"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Antaa sovelluksen lähettää tekstiviestejä. Tästä voi aiheutua odottamattomia kuluja. Haitalliset sovellukset voivat aiheuttaa kuluja lähettämällä viestejä ilman lupaasi."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"lähetä vastaa viestillä -tapahtumia"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Antaa sovelluksen lähettää muille viestisovelluksille pyyntöjä käsitellä saapuvien puheluiden vastaa viestillä -tapahtumia."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lue tekstiviestejä (teksti tai multimedia)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Antaa sovelluksen lukea tablet-laitteeseen tai SIM-kortille tallennettuja tekstiviestejä. Sovellus voi lukea kaikki tekstiviestit huolimatta niiden arkaluonteisuudesta."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Antaa sovelluksen lukea puhelimeen tai SIM-kortille tallennettuja tekstiviestejä. Sovellus voi lukea kaikki tekstiviestit huolimatta niiden arkaluonteisuudesta."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Asettaa toimintojen hallinnan sulkeutumistilaan. Ei sulje puhelinta kokonaan."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"estä sovellusten vaihto"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Estää käyttäjää siirtymästä toiseen sovellukseen."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"hae nykyisen sovelluksen tiedot"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Antaa sovellukselle luvan noutaa nykyistä sovellusta koskevia yksityisiä tietoja ruudun etualalla."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"kaikkien sovellusten käynnistämisen valvonta ja hallinta"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Antaa sovelluksen valvoa ja hallita sitä, miten laite käynnistää toimintoja. Haitalliset sovellukset voivat vaarantaa laitteen käytön. Tätä oikeutta tarvitaan vain kehityskäyttöön eikä koskaan tavalliseen käyttöön."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"lähetä paketeista poistettuja lähetyksiä"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Antaa sovelluksen lukea nykyisiä alhaisen tason akunkäyttötietoja. Sovellus saattaa saada tietoonsa, mitä sovelluksia käytät."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"muokkaa akkutilastoja"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Antaa sovelluksen muokata kerättyjä akkutilastoja. Ei tavallisten sovellusten käyttöön."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"nouda sovellusten toimintatilastoja"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Antaa sovelluksen noutaa kerättyjä sovellusten toimintatilastoja. Ei tavallisten sovellusten käyttöön."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"sovellusten käyttötilastojen muokkaus"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Antaa sovelluksen muokata kerättyjä sovellusten käyttötilastoja. Ei tavallisten sovellusten käyttöön."</string> <string name="permlab_backup" msgid="470013022865453920">"hallitse järjestelmän varmuuskopiointia ja palauttamista"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Antaa sovelluksen hallita järjestelmän varmuuskopiointi- ja palautusmekanismia. Ei tavallisten sovellusten käyttöön."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"vahvista täysi varmuuskopiointi tai palauta toiminto"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Antaa sovelluksen sitoutua syötetavan ylätason käyttöliittymään. Ei tavallisten sovellusten käyttöön."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sitoudu esteettömyyspalveluun"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Antaa sovelluksen sitoutua esteettömyyspalvelun ylemmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"pyydä kosketuksella tutkimisen käyttöönottoa"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Antaa luvan haltijan pyytää käyttöön vuorovaikutustilaa, jossa kosketettujen kohteiden nimet sanotaan ääneen ja käyttöliittymää voi tutkia eleiden avulla."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"pyydä parannettua verkon esteettömyyttä"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Antaa luvan haltijan pyytää verkon esteettömyysparannuksien käyttöönottoa. Sovellus voi esimerkiksi pyytää Googlen koodikatkelmien asentamista sovelluksen sisällön esteettömyyden parantamiseksi."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"tekstipalveluun sitoutuminen"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Antaa sovelluksen sitoutua tekstipalvelun (kuten SpellCheckerServicen) ylätason liittymään. Ei tavallisten sovellusten käyttöön."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"sitoudu VPN-palveluun"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Sallii sovelluksen hallinnoida verkkokäytäntöjä ja määritellä sovelluskohtaisia sääntöjä."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verkon käytön seurannan muokkaaminen"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Antaa sovelluksen muokata, miten sovellusten verkonkäyttöä lasketaan. Ei tavallisten sovellusten käyttöön."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"käytä ilmoituksia"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Antaa sovelluksen noutaa, tutkia ja tyhjentää ilmoituksia (myös muiden sovelluksien lähettämiä)."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Aseta salasanasäännöt"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Hallinnoi ruudun lukituksenpoistosalasanoissa sallittuja merkkejä ja salasanan pituutta."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Tarkkaile ruudun lukituksen poistoyrityksiä"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Kuvio tyhjennetty"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Solu lisätty"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Kuvio valmis"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d/%3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Lisää widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tyhjä"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Lukituksen poiston alue laajennettu."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 550e09c..c94946e 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Accéder à la mémoire de stockage USB"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Accéder à la carte SD"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Fonctionnalités d\'accessibilité"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Fonctionnalités pouvant être requises dans le cadre des technologies d\'assistance"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"Désactivation ou modification de la barre d\'état"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barre d\'état"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permet à l\'application de lire les messages que votre appareil reçoit via un canal de diffusion cellulaire. Dans certaines zones géographiques, des alertes vous sont envoyées afin de vous prévenir en cas de situation d\'urgence. Les applications malveillantes peuvent venir perturber les performances ou le fonctionnement de votre appareil lorsqu\'un message est reçu via un canal de diffusion cellulaire."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"Envoi de messages SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permet à l\'application d\'envoyer des messages SMS. Cette autorisation peut entraîner des frais inattendus. Les applications malveillantes peuvent générer des frais en envoyant des messages sans votre consentement."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"envoyer des SMS sans confirmation"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permet à l\'application d\'envoyer des messages SMS. Cette autorisation peut entraîner des frais inattendus. Les applications malveillantes peuvent générer des frais en envoyant des messages sans votre consentement."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"envoyer des réponses par message"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permet à l\'application d\'envoyer à d\'autres applications de SMS/MMS des demandes pour gérer les réponses par message pour les appels entrants."</string> <string name="permlab_readSms" msgid="8745086572213270480">"voir les messages texte (SMS ou MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permet à l\'application de lire les SMS stockés sur votre tablette ou sur la carte SIM. Cette autorisation lui permet de lire tous les SMS, indépendamment de leur contenu ou de leur caractère confidentiel."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permet à l\'application de lire les SMS stockés sur votre téléphone ou sur la carte SIM. Cette autorisation lui permet de lire tous les SMS, indépendamment de leur contenu ou de leur caractère confidentiel."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Place le gestionnaire d\'activités en état d\'arrêt. N\'effectue pas un arrêt complet."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"empêcher les changements d\'applications"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Empêche l\'utilisateur de changer d\'application."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"Récupérer des informations sur l\'application actuelle"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permet à l\'application autorisée de récupérer des informations confidentielles à propos de l\'application exécutée au premier plan sur l\'écran."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"suivre et contrôler le lancement de toutes les applications"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permet à l\'application de surveiller et de contrôler la façon dont le système lance les activités. Des applications malveillantes peuvent exploiter cette fonctionnalité pour totalement compromettre le système. Cette autorisation est uniquement destinée aux développeurs. Elle ne doit jamais être activée dans le cadre d\'une utilisation standard."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"Envoyer une diffusion sans paquet"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permet à une application de lire les données de consommation actuelles indiquant le faible niveau de la batterie. Permet éventuellement à l\'application d\'obtenir des informations détaillées sur les applications que vous utilisez."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifier les statistiques de la batterie"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permet à l\'application de modifier les statistiques collectées concernant la batterie. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"récupérer les statistiques de fonctionnement des applications"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permet à l\'application de récupérer les statistiques de fonctionnement des applications recueillies. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifier les statistiques de fonctionnement des applications"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permet à l\'application de modifier les statistiques de fonctionnement des applications collectées. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string> <string name="permlab_backup" msgid="470013022865453920">"contrôler la sauvegarde et la restauration du système"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permet à l\'application de contrôler le mécanisme de sauvegarde et de restauration du système. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmer une sauvegarde complète ou une restauration"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un mode de saisie. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"associer à un service d\'accessibilité"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service d\'accessibilité. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"demander l\'activation de la fonctionnalité \"Explorer au toucher\""</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Permet à l\'application de demander l\'activation d\'un mode d\'interaction dans lequel les éléments sur lesquels l\'utilisateur appuie sont énoncés à voix haute et dans lequel l\'utilisateur peut explorer l\'interface à l\'aide de gestes."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"demander des améliorations de la fonctionnalité d\'accessibilité Web"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Permet à l\'application de demander l\'activation d\'améliorations de la fonctionnalité d\'accessibilité Web, telles que l\'installation de scripts pour rendre le contenu des applications plus accessible."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"associer à un service de texte"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permet à l\'application de s\'associer à l\'interface de haut niveau d\'un service de texte (par exemple, SpellCheckerService). Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"associer à un service VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permet à l\'application de gérer les stratégies du réseau et de définir celles qui sont spécifiques à l\'application."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modifier le système de comptabilisation de l\'utilisation du réseau"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permet à l\'application de modifier l\'utilisation du réseau par les autres applications. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"accéder aux notifications"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet aux applications de récupérer, d\'examiner et d\'autoriser les notifications, y compris celles envoyées par d\'autres applications."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Définir les règles du mot de passe"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Choisir le nombre et le type de caractères autorisés dans les mots de passe de déverrouillage de l\'écran"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Gérer les tentatives de déverrouillage de l\'écran"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Schéma effacé."</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Cellule ajoutée."</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Schéma terminé."</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d sur %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Ajouter un widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vide"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Zone de déverrouillage développée."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 11972fa..3521f4b 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"संग्रहण"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"USB संग्रहण में पहुंचें."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SD कार्ड में पहुंचें."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"पहुंच-योग्यता सुविधाएं"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"सहायक प्रौद्योगिकी के द्वारा अनुरोध की जा सकने वाली सुविधाएं."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति बार अक्षम या बदलें"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"एप्लिकेशन को स्थिति बार अक्षम करने या सिस्टम आइकन को जोड़ने या निकालने देता है."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"स्थिति बार"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"एप्लिकेशन को आपके उपकरण द्वारा प्राप्त सेल प्रसारण संदेशों को पढ़ने देता है. कुछ स्थानों पर आपको आपातकालीन स्थितियों की चेतावनी देने के लिए सेल प्रसारण अलर्ट वितरित किए जाते हैं. आपातकालीन सेल प्रसारण प्राप्त होने पर दुर्भावनापूर्ण एप्लिकेशन आपके उपकरण के निष्पादन या संचालन में हस्तक्षेप कर सकते हैं."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMS संदेश भेजें"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"एप्लिकेशन को SMS संदेशों को भेजने देता है. इसके परिणामस्वरूप अप्रत्याशित शुल्क लागू हो सकते हैं. दुर्भावनापूर्ण एप्लिकेशन आपकी पुष्टि के बिना संदेश भेजकर आपका धन व्यय कर सकते हैं."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"बिना किसी पुष्टि के SMS संदेश भेजें"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"एप्लिकेशन को SMS संदेशों को भेजने देता है. इसके परिणामस्वरूप अप्रत्याशित शुल्क लागू हो सकते हैं. दुर्भावनापूर्ण एप्लिकेशन आपकी पुष्टि के बिना संदेश भेजकर आपका धन व्यय कर सकते हैं."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"संदेश-द्वारा-जवाब भेजें ईवेंट"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"इनकमिंग कॉल के संदेश-द्वारा-जवाब देने के ईवेंट प्रबंधित करने के लिए, एप्लिकेशन को अन्य संदेश सेवा एप्लिकेशन को अनुरोध भेजने देती है."</string> <string name="permlab_readSms" msgid="8745086572213270480">"अपने पाठ संदेश (SMS या MMS) पढ़ें"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"एप्लिकेशन को आपके टेबलेट या SIM कार्ड में संग्रहीत SMS संदेश पढ़ने देता है. इससे सामग्री या गोपनीयता पर ध्यान दिए बिना, एप्लिकेशन सभी SMS संदेश पढ़ सकता है."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"एप्लिकेशन को आपके फ़ोन या SIM कार्ड में संग्रहीत SMS संदेश पढ़ने देता है. इससे सामग्री या गोपनीयता पर ध्यान दिए बिना, एप्लिकेशन सभी SMS संदेश पढ़ सकता है."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"गतिविधि प्रबंधक को शटडाउन स्थिति में रखता है. पूर्ण शटडाउन निष्पादित नहीं करता है."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"एप्लिकेशन स्विच करने से रोकता है"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"उपयोगकर्ता को दूसरे एप्लिकेशन पर स्विच करने से रोकता है."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"वर्तमान एप्लिकेशन की जानकारी प्राप्त करें"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"धारक को स्क्रीन के अग्रभाग में स्थित वर्तमान एप्लिकेशन के बारे में निजी जानकारी प्राप्त करने देती है."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"सभी एप्लिकेशन की लॉन्चिंग की निगरानी करें और उसे नियंत्रित करें"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"एप्लिकेशन को यह निगरानी और नियंत्रित करने देता है कि सिस्टम कैसे गतिविधियां लॉन्च करता है. दुर्भावनापूर्ण एप्लिकेशन सिस्टम को पूरी तरह से जोखिम में डाल सकते हैं. इस अनुमति की आवश्यकता केवल विकास के लिए है, सामान्य उपयोग के लिए कभी नहीं."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"पैकेज निकाले गए प्रसारण भेजें"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"एप्लिकेशन को वर्तमान निम्न-स्तरीय बैटरी उपयोग डेटा पढ़ने देती है. एप्लिकेशन को आपके द्वारा उपयोग किए जाने वाले एप्लिकेशन के बारे में विस्तृत जानकारी ढूंढने दे सकती है."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"बैटरी के आंकड़े संशोधित करें"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"एप्लिकेशन को बैटरी के संकलित आंकड़ों को संशोधित करने देती है. सामान्य एप्लिकेशन के द्वारा उपयोग करने के लिए नहीं."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"एप्लिकेशन संचालन आंकड़े प्राप्त करें"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"एप्लिकेशन को संकलित एप्लिकेशन संचालन आंकड़े प्राप्त करने देता है. सामान्य एप्लिकेशन के द्वारा उपयोग के लिए नहीं."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"एप्लिकेशन कार्यवाही के आंकड़े बदलें"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"एप्लिकेशन को एप्लिकेशन कार्यवाही के एकत्रित आंकड़े बदलने देता है. सामान्य एप्लिकेशन के द्वारा उपयोग करने के लिए नहीं."</string> <string name="permlab_backup" msgid="470013022865453920">"सिस्टम बैकअप नियंत्रित और पुनर्स्थापित करें"</string> <string name="permdesc_backup" msgid="6912230525140589891">"एप्लिकेशन को सिस्टम के बैकअप को नियंत्रित और क्रियाविधि को पुर्नस्थापित करने देता है. सामान्य एप्लिकेशन द्वारा उपयोग करने के लिए नहीं."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"पूर्ण बैकअप या पुनर्स्थापना कार्यवाही की पुष्टि करें"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"धारक को किसी इनपुट विधि के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"पहुंच-योग्यता सेवा से आबद्ध करें"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"धारक को किसी पहुंच-योग्यता सेवा के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"स्पर्श द्वारा एक्सप्लोर करें का अनुरोध करें"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"धारक को ऐसे सहभागी मोड का अनुरोध करने देती है जिसमें स्पर्श किए गए आइटम ज़ोर से बोले जाते हैं और UI को जेस्चर के द्वारा एक्सप्लोर किया जा सकता है."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"उन्नत वेब पहुंच-योग्यता का अनुरोध करें"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"धारक को वेब पहुंच-योग्यता एन्हांसमेंट सक्षम करने का अनुरोध करने देती है. उदाहरण के लिए, एप्लिकेशन सामग्री को अधिक पहुंच-योग्य बनाने के लिए स्क्रिप्ट इंस्टॉल करना."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"किसी पाठ सेवा पर बने रहें"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"धारक को किसी पाठ सेवा (उदा. SpellCheckerService) के शीर्ष-स्तर इंटरफ़ेस पर आबद्ध होने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"किसी VPN सेवा से आबद्ध करें"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"एप्लिकेशन को नेटवर्क नीतियां प्रबंधित करने और एप्लिकेशन-विशिष्ट नियमों को परिभाषित करने देता है."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क उपयोग हिसाब बदलें"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"एप्लिकेशन को यह संशोधित करने देता है कि एप्लिकेशन की तुलना में नेटवर्क उपयोग का मूल्यांकन कैसे किया जाता है. सामान्य एप्लिकेशन द्वारा उपयोग करने के लिए नहीं."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"सूचनाओं तक पहुंचें"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"एप्लिकेशन को सूचनाओं को प्राप्त करने, जांच करने, और साफ़ करने देता है, जिनमें अन्य एप्लिकेशन के द्वारा पोस्ट की गई सूचनाएं भी शामिल हैं."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करें"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"स्क्रीन-अनलॉक पासवर्ड में अनुमति प्राप्त लंबाई और वर्णों को नियंत्रित करें."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"स्क्रीन-अनलॉक के प्रयासों पर निगरानी रखें"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"प्रतिमान साफ़ किया गया"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"कक्ष जोड़ा गया"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"प्रतिमान पूरा किया गया"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %3$d विजेट में से %2$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"विजेट जोड़ें"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"रिक्त"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"अनलॉक क्षेत्र को विस्तृत कर दिया गया."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index c13dd42..ce7e954 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Prostor za pohranu"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Pristupi memoriji USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Pristup SD kartici."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Značajke dostupnosti"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Značajke koje tehnologija za pristupačnost može zahtijevati."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmjena trake statusa"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"traka statusa"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Omogućuje aplikaciji čitanje poruka emitiranih unutar mobilne mreže koje prima vaš uređaj. Upozorenja koja se emitiraju na području mobilne mreže dostavljaju se na nekim lokacijama kako bi upozorila korisnike na hitne situacije. Zlonamjerne aplikacije mogu ometati izvršavanje ili rad vašeg uređaja kada stigne hitno upozorenje koje se emitira unutar mobilne mreže."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"slanje SMS poruka"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Aplikaciji omogućuje slanje SMS poruka. To može dovesti do neočekivanih troškova. Zlonamjerne aplikacije mogu vam uzrokovati dodatne troškove slanjem poruka bez vašeg odobrenja."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"slanje SMS poruka bez potvrde"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Aplikaciji omogućuje slanje SMS poruka. To može dovesti do neočekivanih troškova. Zlonamjerne aplikacije mogu vam uzrokovati dodatne troškove slanjem poruka bez vašeg odobrenja."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"slanje događaja \"odgovaranja porukom\""</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Omogućuje aplikaciji slanje zahtijeva drugim aplikacijama za primanje i slanje poruka radi obrade događaja \"odgovaranja porukom\" za dolazne pozive."</string> <string name="permlab_readSms" msgid="8745086572213270480">"čitanje tekstnih poruka (SMS ili MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Aplikaciji omogućuje čitanje SMS poruka pohranjenih na tabletnom računalu ili SIM kartici. To aplikaciji omogućuje čitanje svih SMS poruka, neovisno o sadržaju ili povjerljivosti."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Aplikaciji omogućuje čitanje SMS poruka pohranjenih na telefonu ili SIM kartici. To aplikaciji omogućuje čitanje svih SMS poruka, neovisno o sadržaju ili povjerljivosti."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Postavlja upravitelja za aktivnost u stanje mirovanja. Ne isključuje ga u potpunosti."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"sprečavanje promjene aplikacije"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Sprječava korisnika u prebacivanju na drugu aplikaciju."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"dohvaćanje informacija o trenutačnoj aplikaciji"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Omogućuje nositelju dohvaćanje privatnih informacija o trenutačnoj aplikaciji u prednjem planu na zaslonu."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"praćenje i nadzor svih pokretanja aplikacija"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Omogućuje aplikaciji nadzor i upravljanje načinom na koji sustav pokreće aktivnosti. Zlonamjerne aplikacije mogu posve ugroziti sustav. Ta je dozvola potrebna samo za razvoj, nikada za uobičajenu upotrebu."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"slanje paketno uklonjenog prijenosa"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Aplikacija može očitavati podatke o trenutačnoj potrošnji baterije na niskoj razini. Tako može doznati detaljne informacije o aplikacijama koje upotrebljavate."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"izmjena statistike o bateriji"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Omogućuje aplikaciji promjenu prikupljene statistike o potrošnji baterije. Nije namijenjena uobičajenim aplikacijama."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"dohvaćanje statistike o radu aplikacije"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Aplikaciji omogućuje dohvaćanje prikupljene statistike o radu aplikacije. Nije namijenjena uobičajenim aplikacijama."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"izmjena statistike o radu aplikacije"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Aplikaciji omogućuje promjenu prikupljene statistike o radu aplikacije. Nije namijenjena uobičajenim aplikacijama."</string> <string name="permlab_backup" msgid="470013022865453920">"sigurnosna kopija i oporavak nadzornog sustava"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Omogućuje aplikaciji upravljanje mehanizmom stvaranja sigurnosnih kopija i obnove sustava. Nije namijenjena uobičajenim aplikacijama."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potvrditi postupak izrade sigurnosne kopije ili obnove"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Nositelju omogućuje povezivanje sučelja najviše razine načina unosa. Ne bi smjelo biti potrebno za normalne aplikacije."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vezivanje uz uslugu dostupnosti"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge dostupnosti. Ne bi smjelo biti potrebno za normalne aplikacije."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"zahtijevaj istraživanje dodirom"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Omogućuje korisniku zahtijevanje načina interakcije u kojem se dodirnute stavke izgovaraju naglas, a korisničko sučelje može se istraživati pokretima."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"zahtijevaj poboljšanu web-dostupnost"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Omogućuje korisniku zahtijevanje omogućivanja poboljšanja za dostupnost na webu. Na primjer, instaliranje skripti kako bi sadržaj aplikacije bio pristupačniji."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"vezanje na tekstualnu uslugu"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Omogućuje korisniku povezivanje s najvišom razinom sučelja tekstualne usluge (npr. SpellCheckerService). Ne bi smjelo biti potrebno za normalne aplikacije."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"vezanje na VPN uslugu"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Aplikaciji omogućuje upravljanje mrežnim pravilima i određivanje pravila za aplikacije."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"izmjena evidencije mrežne upotrebe"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Omogućuje aplikaciji izmjenu načina upotrebe mreže u odnosu na aplikacije. Nije namijenjeno uobičajenim aplikacijama."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"pristup obavijestima"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Omogućuje aplikaciji dohvaćanje, pregledavanje i brisanje obavijesti, uključujući obavijesti drugih aplikacija."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Postavi pravila zaporke"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Upravljajte duljinom zaporki za otključavanje zaslona i dopuštenim znakovima u tim zaporkama."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Nadgledaj pokušaje otključavanja zaslona"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Uzorak je obrisan"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Dodan je mobitel"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Uzorak je dovršen"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d od %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Dodavanje widgeta."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prazno"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Područje za otključavanje prošireno je."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index e1e7901..d74810a 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Tárhely"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Az USB-tár elérése."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Az SD-kártya elérése."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Kisegítő lehetőségek funkciói"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"A kisegítő technológia által kérhető funkciók."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"állapotsor kikapcsolása vagy módosítása"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Lehetővé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"állapotsor"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Lehetővé teszi az alkalmazás számára az eszközre érkező cellán belüli üzenetek olvasását. Bizonyos helyeken figyelmeztető üzeneteket kaphat a cellán belül a vészhelyzetekről. A rosszindulatú alkalmazások befolyásolhatják az eszköz teljesítményét vagy működését vészhelyzeti cellaüzenet érkezésekor."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMS-ek küldése"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Lehetővé teszi az alkalmazás számára, hogy SMS-eket küldjön. Ennek eredményeként váratlan terhelésekkel találkozhat. A rosszindulatú alkalmazások az Ön jóváhagyása nélkül küldhetnek üzeneteket, így költségek merülhetnek fel."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"SMS üzenetek küldése megerősítés nélkül"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Lehetővé teszi az alkalmazás számára, hogy SMS-eket küldjön. Ennek eredményeként váratlan terhelésekkel találkozhat. A rosszindulatú alkalmazások az Ön jóváhagyása nélkül küldhetnek üzeneteket, így költségek merülhetnek fel."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"válaszküldés üzenetben"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Lehetővé teszi az alkalmazás számára, hogy kérelmeket küldjön más üzenetküldő alkalmazásoknak, azért hogy kezelje az üzenetben történő válaszküldést a bejövő hívások esetében."</string> <string name="permlab_readSms" msgid="8745086572213270480">"szöveges üzenetek (SMS vagy MMS) olvasása"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen a táblagépen vagy SIM kártyán tárolt SMS-ekhez. Így az alkalmazás hozzáférhet az összes SMS-hez, azok tartalmától és titkos jellegétől függetlenül."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen a telefonon vagy SIM kártyán tárolt SMS-ekhez. Így az alkalmazás hozzáférhet az összes SMS-hez, azok tartalmától és titkos jellegétől függetlenül."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Leállítás állapotba helyezi a tevékenységkezelőt. Nem hajtja végre a teljes leállítást."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"alkalmazásváltás megakadályozása"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Megakadályozza, hogy a felhasználó átváltson egy másik alkalmazásra."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"az alkalmazás aktuális információinak lekérése"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Lehetővé teszi, hogy a felhasználó privát adatokat kérjen le az aktuális alkalmazásról a képernyő előterében."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"alkalmazásindítások nyomon követése és vezérlése"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Lehetővé teszi az alkalmazás számára, hogy figyelje és vezérelje, hogy a rendszer hogyan indít el tevékenységeket. A rosszindulatú alkalmazások teljesen tönkretehetik a rendszert. Ez az engedély csak fejlesztéshez szükséges, normál használathoz sosem."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"eltávolított csomagú üzenetek küldése"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Lehetővé teszi egy alkalmazás számára, hogy leolvassa az aktuális alacsony szintű akkumulátorhasználatra vonatkozó adatokat. Ezáltal az alkalmazás részletes adatokhoz jut az Ön által használt alkalmazásokról."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"akkumulátorstatisztikák módosítása"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Lehetővé teszi az alkalmazás számára az összegyűjtött akkumulátorhasználati statisztikák módosítását. Normál alkalmazások nem használhatják."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"alkalmazásműveleti statisztikák lekérdezése"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Lehetővé teszi az alkalmazás számára az összegyűjtött alkalmazásműveleti statisztikák lekérdezését. Normál alkalmazások nem használhatják."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"alkalmazásműveleti statisztikák módosítása"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Lehetővé teszi az alkalmazás számára az összegyűjtött alkalmazásműveleti statisztikák módosítását. Normál alkalmazások nem használhatják."</string> <string name="permlab_backup" msgid="470013022865453920">"rendszer biztonsági mentésének és helyreállításának vezérlése"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Lehetővé teszi az alkalmazás számára a rendszer biztonsági mentési és visszaállítási mechanizmusának vezérlését. Normál alkalmazások nem használhatják."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"teljes biztonsági mentés vagy helyreállítási művelet megerősítése"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lehetővé teszi, hogy a tulajdonos kötelezővé tegye egy beviteli mód legfelső szintű felületét. A normál alkalmazásoknak erre soha nincs szüksége."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"csatlakozás egy kisegítő szolgáltatáshoz"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lehetővé teszi a használó számára, hogy csatlakozzon egy kisegítő szolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"felfedezés érintéssel kérése"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Lehetővé teszi a felhasználó számára egy olyan használati mód engedélyezését, amelyben az eszköz kimondja, hogy milyen elemek lettek megérintve, a felhasználói felület pedig felfedezhető kézmozdulatok révén."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"bővített internetes kisegítő lehetőségek kérése"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Lehetővé teszi a felhasználó számára az internetes kisegítő lehetőségek bővítését. Telepíthet például szkripteket, hogy még inkább elérhetővé tegye az alkalmazás tartalmát."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"csatlakozás szövegszolgáltatáshoz"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Lehetővé teszi, hogy a tulajdonos egy szöveges szolgáltatás felső szintjéhez kapcsolódjon (pl. SpellCheckerService). A normál alkalmazásoknak erre soha nincs szüksége."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"csatlakozás egy VPN-szolgáltatáshoz"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Lehetővé teszi az alkalmazás számára, hogy kezelje a hálózati irányelveket és meghatározza az alkalmazásspecifikus szabályokat."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"hálózathasználat elszámolásának módosítása"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy a hálózathasználatot hogyan számolják el az alkalmazások esetében. Normál alkalmazások nem használhatják."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"hozzáférési értesítések"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Lehetővé teszi, hogy az alkalmazás értesítéseket kérdezzen le, vizsgáljon és tisztítson meg, beleértve az egyéb alkalmazások által közzétett értesítéseket is."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Jelszavakkal kapcsolatos szabályok beállítása"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"A képernyőzár-feloldási jelszavakban engedélyezett karakterek és hosszúság vezérlése."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Képernyőzár-feloldási kísérletek figyelése"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Minta törölve"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Cella hozzáadva"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Minta befejezve"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Modul %3$d/%2$d"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Modul hozzáadása."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Üres"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Feloldási terület kiterjesztve."</string> @@ -1047,7 +1062,7 @@ <string name="noApplications" msgid="2991814273936504689">"Egy alkalmazás sem tudja végrehajtani ezt a műveletet."</string> <string name="aerr_title" msgid="1905800560317137752"></string> <string name="aerr_application" msgid="932628488013092776">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazás sajnos leállt."</string> - <string name="aerr_process" msgid="4507058997035697579">"Sajnos a(z) <xliff:g id="PROCESS">%1$s</xliff:g> folyamat leállt."</string> + <string name="aerr_process" msgid="4507058997035697579">"Sajnos a <xliff:g id="PROCESS">%1$s</xliff:g> alkalmazás leállt."</string> <string name="anr_title" msgid="4351948481459135709"></string> <string name="anr_activity_application" msgid="1904477189057199066">"A(z) <xliff:g id="APPLICATION">%2$s</xliff:g> nem válaszol."\n\n"Szeretné bezárni?"</string> <string name="anr_activity_process" msgid="5776209883299089767">"A(z) <xliff:g id="ACTIVITY">%1$s</xliff:g> tevékenység nem válaszol."\n\n"Szeretné bezárni?"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index d6a16ca..d0b4d61 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Penyimpanan"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Akses penyimpanan USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Akses kartu SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Fitur aksesibilitas"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Fitur yang dapat diminta oleh teknologi bantu."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"nonaktifkan atau ubah bilah status"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"bilah status"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Mengizinkan aplikasi membaca pesan siaran seluler yang diterima perangkat Anda. Lansiran siaran seluler dikirimkan di beberapa lokasi untuk memperingatkan Anda tentang situasi darurat. Aplikasi berbahaya dapat mengganggu kinerja atau operasi perangkat Anda saat siaran seluler darurat diterima."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"kirim pesan SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Memungkinkan aplikasi mengirim pesan SMS. Izin ini dapat mengakibatkan biaya tak terduga. Aplikasi berbahaya dapat membebankan biaya kepada Anda dengan mengirim pesan tanpa konfirmasi dari Anda."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"mengirim SMS tanpa konfirmasi"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Memungkinkan aplikasi mengirim pesan SMS. Izin ini dapat mengakibatkan biaya tak terduga. Aplikasi berbahaya dapat membebankan biaya kepada Anda dengan mengirim pesan tanpa konfirmasi dari Anda."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"kirim acara tanggapi-lewat-pesan"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Mengizinkan aplikasi mengirimkan permintaan ke aplikasi perpesanan lainnya guna menangani acara tanggapi-lewat-pesan untuk panggilan masuk."</string> <string name="permlab_readSms" msgid="8745086572213270480">"membaca pesan teks (SMS atau MMS) Anda"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Memungkinkan aplikasi membaca pesan SMS yang tersimpan di tablet atau kartu SIM Anda. Izin ini memungkinkan aplikasi membaca semua pesan SMS, terlepas dari konten atau kerahasiaan."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Memungkinkan aplikasi membaca pesan SMS yang tersimpan di ponsel atau kartu SIM Anda. Izin ini memungkinkan aplikasi membaca semua pesan SMS, terlepas dari konten atau kerahasiaan."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Letakkan pengelola aktivitas dalam kondisi mati. Tidak melakukan penonaktifan penuh."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"cegah pergantian aplikasi"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Mencegah pengguna beralih ke apl lain."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"dapatkan info tentang aplikasi yang aktif"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Memungkinkan pemegang mengambil informasi pribadi tentang aplikasi yang aktif di latar depan layar."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"memantau dan mengontrol semua peluncuran apl"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Mengizinkan apl memantau dan mengontrol cara sistem meluncurkan kegiatan. Apl berbahaya dapat meretas sistem sepenuhnya. Izin ini hanya diperlukan untuk pengembangan, tidak pernah diperlukan untuk penggunaan normal."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"kirim siaran paket dihapus"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Mengizinkan aplikasi membaca data penggunaan baterai tingkat rendah. Dapat mengizinkan aplikasi mencari informasi mendetail tentang aplikasi mana yang Anda gunakan."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"ubah statistik baterai"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Mengizinkan aplikasi mengubah statistik baterai yang dikumpulkan. Tidak untuk digunakan oleh aplikasi normal."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"mengambil statistik pengoperasian aplikasi"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Mengizinkan aplikasi mengambil statistik pengoperasian aplikasi yang dikumpulkan. Tidak untuk digunakan oleh aplikasi normal."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"memodifikasi statistik pengoperasian aplikasi"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Mengizinkan aplikasi memodifikasi statistik pengoperasian aplikasi yang dikumpulkan. Tidak untuk digunakan oleh aplikasi normal."</string> <string name="permlab_backup" msgid="470013022865453920">"mengontrol cadangan dan pemulihan sistem"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Mengizinkan apl mengontrol mekanisme pencadangan dan pemulihan sistem. Tidak untuk digunakan oleh apl normal."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"konfirmasi pencadangan penuh atau pulihkan operasi"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu metode masukan. Tidak pernah diperlukan oleh apl normal."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"mengikat ke layanan aksesibilitas"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Mengizinkan pemegang untuk mengikat antarmuka tingkat tinggi dari suatu layanan. Tidak pernah diperlukan oleh aplikasi normal."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"meminta penjelajahan dengan sentuhan"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Memungkinkan pemegang meminta mode interaksi yang mana item yang disentuh diucapkan dengan keras dan UI dapat dijelajahi dengan isyarat."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"meminta aksesibilitas web yang disempurnakan"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Memungkinkan pemegang meminta pengaktifan penyempurnaan aksesibilitas web. Contohnya, memasang skrip agar konten aplikasi lebih mudah diakses."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"mengikat ke layanan SMS"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan teks (mis. SpellCheckerService). Tidak pernah diperlukan oleh apl normal."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"mengikat ke layanan VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Mengizinkan apl mengelola kebijakan jaringan dan menentukan peraturan khusus apl."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"mengubah penghitungan penggunaan jaringan"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Mengizinkan apl memodifikasi cara penggunaan jaringan diperhitungkan terhadap apl. Tidak untuk digunakan oleh apl normal."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"mengakses pemberitahuan"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Mengizinkan aplikasi mengambil, memeriksa, dan menghapus pemberitahuan, termasuk pemberitahuan yang diposkan oleh aplikasi lain."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Setel aturan sandi"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrol panjang dan karakter yang diizinkan dalam sandi pembuka layar."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Upaya pembukaan kunci layar monitor"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Pola dihapus"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Sel ditambahkan"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Pola selesai"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d dari %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Tambahkan widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Kosong"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Area buka kunci diluaskan."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 2257daa..87d21fe 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Archiviazione"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Accesso all\'archivio USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Accesso alla scheda SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funzioni di accessibilità"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funzioni che possono essere richieste dalla tecnologia di ausilio."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"disattivare o modificare la barra di stato"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barra di stato"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le applicazioni dannose potrebbero interferire con il rendimento o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"invio SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Consente all\'applicazione di inviare messaggi SMS. Ciò potrebbe comportare costi imprevisti. Applicazioni dannose potrebbero generare dei costi inviando messaggi senza la tua conferma."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"invio di messaggi SMS senza conferma"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Consente all\'applicazione di inviare messaggi SMS. Ciò potrebbe comportare costi imprevisti. Applicazioni dannose potrebbero generare dei costi inviando messaggi senza la tua conferma."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"invio di eventi di risposta tramite messaggio"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Consente all\'app di inviare richieste ad altre app di messaggi per gestire eventi di risposta tramite messaggio per le chiamate in arrivo."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lettura messaggi di testo personali (SMS o MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Consente all\'applicazione di leggere i messaggi SMS memorizzati sul tablet o sulla scheda SIM. Ciò consente all\'applicazione di leggere tutti i messaggi SMS, indipendentemente dai contenuti o dal livello di riservatezza."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Consente all\'applicazione di leggere i messaggi SMS memorizzati sul telefono o sulla scheda SIM. Ciò consente all\'applicazione di leggere tutti i messaggi SMS, indipendentemente dai contenuti o dal livello di riservatezza."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Mette il gestore delle attività in uno stato di chiusura. Non esegue una chiusura completa."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedire commutazione applicazione"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impedisce all\'utente di passare a un\'altra applicazione."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"recupero di informazioni sull\'app corrente"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Consente al titolare di recuperare le informazioni private sull\'app correntemente in primo piano sullo schermo."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitoraggio e controllo avvio di tutte le applicazioni"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Consente all\'applicazione di monitorare e controllare l\'avvio delle attività da parte del sistema. Le applicazioni dannose potrebbero compromettere completamente il sistema. Questa autorizzazione è necessaria solo per lo sviluppo, mai per l\'utilizzo normale."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"invio broadcast rimossi dal pacchetto"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Consente a un\'applicazione di leggere i dati di utilizzo della batteria di basso livello correnti. Potrebbe consentire all\'applicazione di trovare informazioni dettagliate sulle applicazioni che utilizzi."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifica statistiche batteria"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Consente all\'applicazione di modificare le statistiche raccolte sulla batteria. Da non usare per normali applicazioni."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"recupero statistiche funzion. app"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Consente all\'applicazione di recuperare le statistiche raccolte sul funzionamento dell\'applicazione. Da non usare per normali applicazioni."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica statistiche funzionamento app"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Consente all\'applicazione di modificare le statistiche raccolte sul funzionamento dell\'applicazione. Da non usare per normali applicazioni."</string> <string name="permlab_backup" msgid="470013022865453920">"controllo del backup di sistema e ripristino"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Consente all\'applicazione di controllare il meccanismo di backup e di ripristino del sistema. Da non usare per normali applicazioni."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"conferma di un\'operazione completa di backup o di ripristino"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Consente l\'associazione di un metodo di inserimento all\'interfaccia principale. Non dovrebbe mai essere necessaria per le normali applicazioni."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"collegamento a un servizio di accessibilità"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Consente al titolare di collegarsi all\'interfaccia di primo livello di un servizio di accessibilità. Non dovrebbe essere mai necessaria per le normali applicazioni."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"richiesta di esplorazione al tocco"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Consente al titolare di richiedere una modalità di interazione con cui gli elementi toccati vengono descritti tramite output vocale e l\'interfaccia utente può essere esplorata tramite gesti."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"richiesta di accessibilità web migliorata"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Consente al titolare di richiedere l\'attivazione dei miglioramenti dell\'accessibilità web, ad esempio l\'installazione di script per rendere più accessibili i contenuti dell\'app."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"associazione a un servizio di testo"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Consente al titolare di collegarsi all\'interfaccia di primo livello di un servizio di testo (ad esempio SpellCheckerService). Non dovrebbe essere mai necessaria per le normali applicazioni."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"associazione a un servizio VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Consente all\'applicazione di gestire le norme di rete e definire le regole specifiche delle applicazioni."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modifica calcolo dell\'utilizzo della rete"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Consente all\'applicazione di modificare il calcolo dell\'utilizzo della rete tra le applicazioni. Da non usare per normali applicazioni."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"accesso a notifiche"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Consente all\'app di recuperare, esaminare e cancellare notifiche, comprese quelle pubblicate da altre app."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Imposta regole password"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlla la lunghezza e i caratteri ammessi nelle password di sblocco dello schermo."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitora tentativi di sblocco dello schermo"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Sequenza cancellata"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Cella aggiunta"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Sequenza completata"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d di %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Aggiungi widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vuoto"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Area di sblocco estesa."</string> @@ -1265,7 +1280,7 @@ <string name="tethered_notification_message" msgid="6857031760103062982">"Tocca per configurare."</string> <string name="back_button_label" msgid="2300470004503343439">"Indietro"</string> <string name="next_button_label" msgid="1080555104677992408">"Avanti"</string> - <string name="skip_button_label" msgid="1275362299471631819">"Salta"</string> + <string name="skip_button_label" msgid="1275362299471631819">"Ignora"</string> <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilizzo dati cell. elevato"</string> <string name="throttle_warning_notification_message" msgid="3340822228599337743">"Tocca per ulteriori informazioni sull\'utilizzo dei dati del cellulare."</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Limite dati cell. superato"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index ce51ef4..d698a77 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"אחסון"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"גישה לאמצעי אחסון מסוג USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"גש לכרטיס SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"תכונות נגישות"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"תכונות שטכנולוגיה מסייעת יכולה לבקש."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"השבת או שנה את שורת המצב"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"מאפשר ליישום להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"שורת מצב"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"מאפשר ליישום לקרוא הודעות שידור סלולרי שהתקבלו במכשיר שלך. התראות שידור סלולרי נשלחות במקומות מסוימים על מנת להזהיר אותך מפני מצבי חירום. יישומים זדוניים עשויים להפריע לביצועים או לפעולה של המכשיר שלך כאשר מתקבל שידור חירום סלולרי."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"שלוח הודעות SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"מאפשר ליישום לשלוח הודעות SMS. הדבר עשוי לגרום לחיובים בלתי צפויים. יישומים זדוניים עלולים לגרום לעלויות על ידי שליחת הודעות ללא אישורך."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"שלח הודעות SMS ללא אישור"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"מאפשר ליישום לשלוח הודעות SMS. הדבר עשוי לגרום לחיובים בלתי צפויים. יישומים זדוניים עלולים לגרום לעלויות על ידי שליחת הודעות ללא אישורך."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"שליחת אירועי \'תגובה באמצעות הודעה\'"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"מאפשר ליישום לשלוח בקשות ליישומים אחרים של העברת הודעות כדי לטפל באירועי \'תגובה באמצעות הודעה\' עבור שיחות נכנסות."</string> <string name="permlab_readSms" msgid="8745086572213270480">"קריאת הודעות הטקסט שלך (SMS או MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"מאפשר ליישום לקרוא הודעות SMS המאוחסנות בטאבלט או בכרטיס ה-SIM. דבר זה מתיר ליישום לקרוא את כל הודעות ה-SMS, ללא התחשבות בתוכן או בסודיות."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"מאפשר ליישום לקרוא הודעות SMS המאוחסנות בטלפון או בכרטיס ה-SIM. דבר זה מתיר ליישום לקרוא את כל הודעות ה-SMS, ללא התחשבות בתוכן או בסודיות."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"מעביר את מנהל הפעילויות למצב כיבוי. לא מבצע כיבוי מלא."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"מנע החלפת יישומים"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"מניעת מעבר ליישום אחר על ידי המשתמש."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"קבל פרטים על היישום הנוכחי"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"מאפשר לבעלים לאחזר מידע פרטי לגבי היישום הנוכחי שבקדמת המסך."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"ניהול מעקב ושליטה על כל הפעלות היישומים"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"מאפשר ליישום לנהל מעקב אחר האופן שבו המערכת מפעילה פעילויות, ולשלוט בו. יישומים זדוניים עלולים לסכן את המערכת כולה. הרשאה זו אינה נחוצה לשימוש רגיל, אלא לפיתוח בלבד."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"שלח שידור שהוסר מחבילה"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"מאפשר ליישום לקרוא את נתוני השימוש הנוכחיים של הסוללה ברמה נמוכה. עשוי לאפשר ליישום לגלות מידע מפורט לגבי היישומים שבהם אתה משתמש."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"שינוי הנתונים הסטטיסטיים של הסוללה"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"מאפשר ליישום לשנות נתונים סטטיסטיים שנאספו לגבי הסוללה. לא מיועד לשימוש על ידי יישומים רגילים."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"אחזור נתונים סטטיסטיים של פעולות יישום"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"הרשאה זו מאפשרת ליישום לאחזר נתונים סטטיסטיים שנאספו לגבי פעולות יישום. לא מיועד לשימוש על ידי יישומים רגילים."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"שינוי סטטיסטיקת ההפעלה של היישום"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"מאפשר ליישום לשנות נתונים סטטיסטיים שנאספו לגבי הפעלתו. לא מיועד לשימוש על ידי יישומים רגילים."</string> <string name="permlab_backup" msgid="470013022865453920">"שלוט בגיבוי ובשחזור של המערכת"</string> <string name="permdesc_backup" msgid="6912230525140589891">"מאפשר ליישום לשלוט במנגנון הגיבוי והשחזור של המערכת. לא מיועד לשימוש על ידי יישומים רגילים."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"אשר פעולה של גיבוי או שחזור מלא"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"מאפשר למשתמש לבצע איגוד לממשק ברמה עליונה של שיטת קלט. הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"הכפפה לשירות נגישות"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"מתיר לבעלים להכפיף לממשק ברמה העליונה של שירות זמינות. הרשאה זו אף פעם אינה אמורה להיות נחוצה ליישומים רגילים."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"בקשת גילוי באמצעות מגע"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"מאפשר לבעלים לבקש מצב אינטראקציה שבו נגיעה בפריטים מלווה בהשמעת התיאור שלהם ושניתן לחקור בו את ה-UI באמצעות מחוות."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"בקשת נגישות משופרת לאינטרנט"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"מאפשר לבעלים לבקש הפעלה של שיפורי נגישות לאינטרנט. לדוגמה, התקנת סקריפטים כדי להגביר את הנגישות של תוכן יישומים."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"הכפפה לשירות טקסט"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"מאפשר למשתמש ליצור איגוד לממשק הרמה העליונה של שירות טקסט (למשל, SpellCheckerService). הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"אגד לשירות VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"מאפשר ליישום לנהל מדיניות הרשת להגדיר כללים ספציפיים-ליישום."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"שנה ניהול חשבונות של שימוש ברשת"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"הרשאה זו מאפשרת ליישום לשנות את אופן החישוב של נתוני שימוש ברשת מול כל יישום. לא מיועד לשימוש ביישומים רגילים."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"גישה להתראות"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"מאפשר ליישום לאחזר, לבדוק ולמחוק התראות, כולל כאלה שפורסמו על ידי יישומים אחרים."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"הגדר כללי סיסמה"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"שלוט באורך ובתווים המותרים בסיסמאות לביטול נעילת מסך."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"עקוב אחר ניסיונות לביטול נעילת מסך"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"התבנית נמחקה"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"התא נוסף"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"התבנית הושלמה"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d מתוך %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"הוסף Widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"ריק"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"אזור ביטול הנעילה הורחב."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index f2ca7c6..fb7ceb9 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"ストレージ"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"USBストレージへのアクセス"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SDカードにアクセスします。"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"ユーザー補助機能"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"補助テクノロジーがリクエストできる機能です。"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"ステータスバーの無効化や変更"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"ステータスバーの無効化、システムアイコンの追加や削除をアプリに許可します。"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"ステータスバーへの表示"</string> @@ -239,12 +241,12 @@ <string name="permdesc_receiveMms" msgid="533019437263212260">"MMSメッセージの受信と処理をアプリに許可します。これにより、アプリが端末に届いたメッセージを表示することなく監視または削除できるようになります。"</string> <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"緊急放送の受信"</string> <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"緊急ブロードキャストメッセージの受信と処理をアプリに許可します。これはシステムアプリのみが利用できる権限です。"</string> - <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"エリアメールSMSの読み取り"</string> - <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"端末で受信したエリアメールSMSの読み取りをアプリに許可します。エリアメール警報は、緊急事態を警告する目的で一部の地域に配信されます。緊急エリアメールの受信時に、悪意のあるアプリによって端末の動作や処理が妨害される恐れがあります。"</string> + <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"緊急警報SMSの読み取り"</string> + <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"端末で受信した緊急警報SMSの読み取りをアプリに許可します。緊急警報は、緊急事態を警告する目的で一部の地域に配信されます。緊急警報の受信時に、悪意のあるアプリによって端末の動作や処理が妨害される恐れがあります。"</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMSメッセージの送信"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"SMSメッセージの送信をアプリに許可します。これにより、予期せぬ料金が発生する可能性があります。悪意のあるアプリが確認なしでメッセージを送信し、料金が発生する恐れがあります。"</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"確認せずにSMSメッセージを送信する"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"SMSメッセージの送信をアプリに許可します。これにより、予期せぬ料金が発生する可能性があります。悪意のあるアプリが確認なしでメッセージを送信し、料金が発生する恐れがあります。"</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"メッセージ応答イベントの送信"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"着信時にメッセージ応答イベントを処理するよう他のSMSアプリにリクエストを送信することをアプリに許可します。"</string> <string name="permlab_readSms" msgid="8745086572213270480">"テキストメッセージ(SMSまたはMMS)の読み取り"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"タブレットまたはSIMカードに保存されているSMSメッセージの読み取りをアプリに許可します。これにより、すべてのSMSメッセージをコンテンツや機密性に関係なくアプリから読み取ることができるようになります。"</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"携帯端末またはSIMカードに保存されているSMSメッセージの読み取りをアプリに許可します。これにより、すべてのSMSメッセージをコンテンツや機密性に関係なくアプリから読み取ることができるようになります。"</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"アクティビティマネージャをシャットダウン状態にします。完全なシャットダウンは実行しません。"</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"アプリケーションの切り替えを禁止する"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"ユーザーが別のアプリに切り替えられないようにします。"</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"現在のアプリ情報の取得"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"画面のフォアグラウンドで現在のアプリに関する非公開情報を取得することを所有者に許可します。"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"すべてのアプリ起動の監視と制御"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"システムによるアクティビティ起動方法を監視し制御することをアプリに許可します。この許可を悪意のあるアプリに利用されると、システム全体のセキュリティが侵害される恐れがあります。この許可は開発時にのみ必要で、通常の使用では不要です。"</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"パッケージ削除ブロードキャストの送信"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"現在の電池消費量の低レベルデータを読み取ることをアプリに許可します。このアプリが、使用しているアプリの詳細情報を確認できるようになります。"</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"電池統計情報の変更"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"電池に関して収集した統計情報の変更をアプリに許可します。通常のアプリでは使用しません。"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"アプリの操作状況に関する統計情報の取得"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"アプリの操作状況に関して収集された統計情報の取得をアプリに許可します。通常のアプリでは使用しません。"</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"アプリの操作状況に関する統計情報の変更"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"アプリの操作状況に関して収集された統計情報の変更をアプリに許可します。通常のアプリでは使用しません。"</string> <string name="permlab_backup" msgid="470013022865453920">"システムのバックアップと復元を制御する"</string> <string name="permdesc_backup" msgid="6912230525140589891">"システムのバックアップと復元メカニズムの制御をアプリに許可します。通常のアプリでは使用しません。"</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"フルバックアップや復元の操作の確認"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"入力方法のトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ユーザー補助サービスにバインド"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ユーザー補助サービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"タッチガイドのリクエスト"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"触れたアイテムが読み上げられ、ジェスチャーでユーザーインターフェースのガイドを利用できる操作モードをリクエストすることを所有者に許可します。"</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"ウェブアクセシビリティ拡張のリクエスト"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"ウェブアクセシビリティの拡張の有効化をリクエストすることを所有者に許可します。たとえば、アプリコンテンツのアクセシビリティをもっと向上させるためにスクリプトをインストールできます。"</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"テキストサービスにバインド"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"テキストサービス(SpellCheckerServiceなど)のトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"VPNサービスにバインド"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"ネットワークポリシーを管理しアプリ固有のルールを定義することをアプリに許可します。"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ネットワークの課金の変更"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"アプリに対するネットワーク利用の計算方法を変更することをアプリに許可します。通常のアプリでは使用しません。"</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"通知にアクセス"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"通知(他のアプリから投稿されたものも含む)を取得、調査、クリアすることをアプリに許可します。"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"パスワードルールの設定"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"画面ロック解除パスワードの長さと使用できる文字を制御します。"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"画面ロック解除試行の監視"</string> @@ -783,11 +797,11 @@ <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ユーザーガイドをご覧いただくか、お客様サポートにお問い合わせください。"</string> <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIMカードはロックされています。"</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIMカードのロック解除中..."</string> - <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"正しくないパスワードを<xliff:g id="NUMBER_0">%d</xliff:g>回入力しました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"正しくないPINを<xliff:g id="NUMBER_0">%d</xliff:g>回入力しました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にGoogleへのログインが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にGoogleへのログインが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒以内にもう一度お試しください。"</string> + <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"正しくないパスワードを<xliff:g id="NUMBER_0">%d</xliff:g>回入力しました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"正しくないPINを<xliff:g id="NUMBER_0">%d</xliff:g>回入力しました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にGoogleへのログインが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にGoogleへのログインが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"タブレットのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、タブレットは工場出荷状態にリセットされ、ユーザーのデータはすべて失われます。"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"端末のロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、端末は工場出荷状態にリセットされ、ユーザーのデータはすべて失われます。"</string> <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"タブレットのロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。タブレットを工場出荷状態にリセットします。"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"パターンを消去しました"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"セルを追加しました"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"パターンの描画が完了しました"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s。ウィジェット%2$d/%3$d。"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ウィジェットを追加します。"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"なし"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"ロック解除エリアを拡大しました。"</string> @@ -1417,7 +1432,7 @@ <string name="kg_wrong_pattern" msgid="1850806070801358830">"パターンが正しくありません"</string> <string name="kg_wrong_password" msgid="2333281762128113157">"パスワードが正しくありません"</string> <string name="kg_wrong_pin" msgid="1131306510833563801">"PINが正しくありません"</string> - <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>秒以内にもう一度お試しください。"</string> + <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>秒後にもう一度お試しください。"</string> <string name="kg_pattern_instructions" msgid="398978611683075868">"パターンを入力"</string> <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PINを入力"</string> <string name="kg_pin_instructions" msgid="2377242233495111557">"PINを入力"</string> @@ -1439,15 +1454,15 @@ <string name="kg_login_invalid_input" msgid="5754664119319872197">"ユーザー名またはパスワードが無効です。"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ユーザー名またはパスワードを忘れた場合は"\n" "<b>"google.com/accounts/recovery"</b>" にアクセスしてください。"</string> <string name="kg_login_checking_password" msgid="1052685197710252395">"アカウントをチェックしています…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"パスワードの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"パスワードの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度お試しください。"</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"タブレットのロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、タブレットは出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string> <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"携帯端末のロック解除に<xliff:g id="NUMBER_0">%d</xliff:g>回失敗しました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回失敗すると、端末は出荷時設定にリセットされ、ユーザーのデータはすべて失われます。"</string> <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"タブレットのロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。タブレットは出荷時設定にリセットされます。"</string> <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"携帯端末のロック解除を<xliff:g id="NUMBER">%d</xliff:g>回失敗しました。端末は出荷時設定にリセットされます。"</string> - <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒以内にもう一度お試しください。"</string> - <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒以内にもう一度お試しください。"</string> + <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、タブレットのロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string> + <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"削除"</string> <string name="safe_media_volume_warning" product="default" msgid="7382971871993371648">"安全レベルを超えるまで音量を上げますか?"\n"大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 3e17643..53fbeb4 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"저장"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"USB 저장소에 액세스합니다."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SD 카드에 액세스합니다."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"접근성 기능"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"장애인 보조 기술이 요청할 수 있는 기능입니다."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"상태 표시줄 사용 중지 또는 수정"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"앱이 상태 표시줄을 사용중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 허용합니다."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"상태 표시줄"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"앱이 기기가 수신한 셀 브로드캐스트 메시지를 읽을 수 있도록 합니다. 비상 상황임을 알리기 위해 일부 지역에서 셀 브로드캐스트 경고가 전달됩니다. 비상 셀 브로드캐스트를 수신할 때 악성 앱이 기기의 성능이나 작동을 방해할 수 있습니다."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMS 메시지 보내기"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"앱이 SMS 메시지를 보낼 수 있도록 허용합니다. 이 경우 예상치 못한 통화 요금이 부과될 수 있습니다. 이 경우 악성 앱이 사용자의 확인 없이 메시지를 전송해 요금이 부과될 수 있습니다."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"확인 없이 SMS 메시지 보내기"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"앱이 SMS 메시지를 보낼 수 있도록 허용합니다. 이 경우 예상치 못한 통화 요금이 부과될 수 있습니다. 이 경우 악성 앱이 사용자의 확인 없이 메시지를 전송해 요금이 부과될 수 있습니다."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"메시지를 통한 응답 이벤트 보내기"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"앱에서 다른 메시징 앱에 요청을 보내어 수신 전화에 대해 메시지를 통한 응답 이벤트를 처리하도록 허용합니다."</string> <string name="permlab_readSms" msgid="8745086572213270480">"내 문자 메시지 읽기(SMS 또는 MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"앱이 태블릿 또는 SIM 카드에 저장된 SMS 메시지를 읽을 수 있도록 허용합니다. 이렇게 하면 앱이 콘텐츠 또는 비밀유지와 관계 없이 모든 SMS 메시지를 읽을 수 있습니다."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"앱이 휴대전화 또는 SIM 카드에 저장된 SMS 메시지를 읽을 수 있도록 허용합니다. 이렇게 하면 앱이 콘텐츠 또는 비밀유지와 관계 없이 모든 SMS 메시지를 읽을 수 있습니다."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"작업 관리자를 종료 상태로 설정합니다. 전체 종료를 수행하지는 않습니다."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"애플리케이션 전환 방지"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"사용자가 다른 앱으로 전환하지 못하게 합니다."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"현재 앱 정보 얻기"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"권한을 가진 프로그램이 화면의 포그라운드에서 현재 애플리케이션에 대한 비공개 정보를 검색하도록 허용합니다."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"실행 중인 모든 앱 모니터링 및 제어"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"앱이 시스템에서 활동이 시작되는 방식을 모니터링하고 관리할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 사용 시에는 필요하지 않습니다."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"패키지 제거 브로드캐스트 보내기"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"애플리케이션이 현재의 낮은 수준 배터리 사용 데이터를 읽을 수 있도록 합니다. 이 경우 애플리케이션이 내가 사용하는 앱에 대한 세부정보를 파악할 수도 있습니다."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"배터리 통계 수정"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"앱이 수집된 배터리 통계를 수정할 수 있도록 허용합니다. 일반 앱에서는 사용하지 않습니다."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"앱 운영 통계 검색"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"앱이 수집된 애플리케이션 운영 통계를 검색하도록 합니다. 일반 앱에서는 사용하지 않습니다."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"앱 운영 통계 수정"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"앱이 수집된 애플리케이션 운영 통계를 수정하도록 합니다. 일반 앱에서는 사용하지 않습니다."</string> <string name="permlab_backup" msgid="470013022865453920">"시스템 백업 및 복원 관리"</string> <string name="permdesc_backup" msgid="6912230525140589891">"앱이 시스템의 백업 및 복원 메커니즘을 제어할 수 있도록 허용합니다. 일반 앱에서는 사용하지 않습니다."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"전체 백업 또는 복원 작업 확인"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"권한을 가진 프로그램이 입력 방법에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"접근성 서비스와 연결"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"권한을 가진 프로그램이 접근성 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"\'터치하여 탐색\' 요청"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"권한을 가진 프로그램이 터치한 항목을 소리내어 말하고 제스처로 UI를 탐색할 수 있는 상호작용 모드를 요청할 수 있도록 허용합니다."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"웹 접근성 개선 요청"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"권한을 가진 프로그램이 웹 접근성 개선을 요청할 수 있도록 허용합니다. 예를 들어 스크립트를 설치하여 앱 콘텐츠에 더 간편하게 액세스할 수 있습니다."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"텍스트 서비스 연결"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"권한을 가진 프로그램이 텍스트 서비스(예: SpellCheckerService)에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"VPN 서비스와 연결"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"앱이 네트워크 정책을 관리하고 앱별 규칙을 정의할 수 있도록 허용합니다."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"네트워크 사용량 계산 수정"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"애플리케이션이 애플리케이션의 네트워크 사용량을 계산하는 방식을 수정할 수 있도록 허용합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"알림 액세스"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"앱이 다른 앱에서 게시한 알림을 비롯하여 알림을 검색하고 살펴보며 삭제할 수 있도록 허용합니다."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"비밀번호 규칙 설정"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"화면 잠금해제 비밀번호에 허용되는 길이 및 문자 수를 제어합니다."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"화면 잠금해제 시도 모니터링"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"패턴 삭제"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"셀 추가됨"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"패턴 완료"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %3$d의 위젯 %2$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"위젯 추가"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"비어 있음"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"잠금 해제 영역 확장됨"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 43ea17d..3b6e440 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Saugykla"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Pasiekti USB atmintinę."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Pasiekite SD kortelę."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Pritaikymo neįgaliesiems funkcijos"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkcijos, kurių užklausas gali teikti pagalbinė technologija."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"išjungti ar keisti būsenos juostą"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Leidžiama programai neleisti būsenos juostos arba pridėti ir pašalinti sistemos piktogramas."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"būsenos juosta"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Programai leidžiama skaityti mobiliuoju transliuojamus pranešimus, gaunamus jūsų įrenginyje. Mobiliuoju transliuojami įspėjimai pristatomi kai kuriose vietose, kad įspėtų apie kritines situacijas. Kai gaunamas mobiliuoju transliuojamas pranešimas apie kritinę situaciją, kenkėjiškos programos gali trukdyti įrenginiui veikti ar jį naudoti."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"siųsti SMS pranešimus"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Leidžiama programai siųsti SMS pranešimus. Dėl to gali atsirasti nenumatytų apmokestinimų. Kenkėjiškos programos gali siųsti mokamus pranešimus be jūsų patvirtinimo."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"siųsti SMS pranešimus be patvirtinimo"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Leidžiama programai siųsti SMS pranešimus. Dėl to gali atsirasti nenumatytų apmokestinimų. Kenkėjiškos programos gali siųsti mokamus pranešimus be jūsų patvirtinimo."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"siųsti atsakymų pranešimu įvykius"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Programai leidžiama siųsti užklausas į kitas susirašinėjimo pranešimais programas, kad būtų galima apdoroti atsakymų pranešimu įvykius, susijusius su gaunamaisiais skambučiais."</string> <string name="permlab_readSms" msgid="8745086572213270480">"skaityti teksto pranešimus (SMS arba MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Leidžiama programai skaityti planšetiniame kompiuteryje ar SIM kortelėje saugomus SMS pranešimus. Taip programai leidžiama skaityti visus SMS pranešimus, neatsižvelgiant į turinį ar konfidencialumą."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Leidžiama programai skaityti telefone ar SIM kortelėje saugomus SMS pranešimus. Taip programai leidžiama skaityti visus SMS pranešimus, neatsižvelgiant į turinį ar konfidencialumą."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Veiklos tvarkyklę perjungia į išsijungimo būseną. Neišjungia visiškai."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"neleisti perjungti programų"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Neleidžiama naudotojui perjungti į kitą programą."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"gauti esamos programos informaciją"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Savininkui leidžiama gauti privačią esamos pirmaeilės ekrano programos informaciją."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"stebėti ir valdyti visų programų paleidimą"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Leidžiama programai stebėti ir valdyti, kaip sistema paleidžia veiklą. Kenkėjiškos programos gali visiškai pažeisti sistemą. Šis leidimas reikalingas tik kuriant ir jo niekada nereikia naudojant įprastai."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"siųsti pašalinto paketo perdavimą"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Leidžiama programai skaityti dabartinius išsikraunančio akumuliatoriaus naudojimo duomenis. Gali būti leidžiama programai sužinoti išsamią informaciją apie jūsų naudojamas programas."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"keisti akumuliatoriaus statistiką"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Leidžiama programai keisti surinktą akumuliatoriaus statistiką. Neskirta naudoti įprastoms programoms."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"nuskaityti programos naudojimo statistiką"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Programai leidžiama nuskaityti surinktą programos naudojimo statistiką. Neskirta naudoti įprastoms programoms."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"keisti programos naudojimo statistiką"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Programai leidžiama keisti surinktą programos naudojimo statistiką. Neskirta naudoti įprastoms programoms."</string> <string name="permlab_backup" msgid="470013022865453920">"valdyti sistemos atsarginę kopiją ir atkūrimą"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Leidžiama programai valdyti sistemos atsarginės kopijos ir atkūrimo mechanizmą. Neskirta naudoti įprastose programose."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"patvirtinkite visos atsarginės kopijos kūrimą arba atkurkite operaciją"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Leidžiama savininką susaistyti su įvesties metodo aukščiausio lygio sąsaja. Įprastoms programoms to neturėtų prireikti."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"susisaistyti su pasiekiamumo paslauga"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Savininkui leidžiama susisaistyti su aukščiausio lygio pasiekiamumo paslaugos sąsaja. Įprastoms programoms to neturėtų prireikti."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"pateikti užklausą dėl naršymo palietus"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Leidžiama pateikti užklausą dėl sąveikos režimo, kuriam veikiant garsiai pasakomi paliesti elementai ir palietimu galima tyrinėti naudotojo sąsają."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"pateikti užklausą dėl patobulintos prieigos prie žiniatinklio"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Leidžiama pateikti užklausą dėl žiniatinklio pritaikymo neįgaliesiems patobulinimų įgalinimo. Pavyzdžiui, pateikti užklausą dėl scenarijų diegimo, kad programos turinys būtų geriau pritaikytas neįgaliesiems."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"priskirti teksto paslaugą"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Leidžiama savininkui priskirti aukščiausio lygio teksto paslaugos (pvz., „SpellCheckerService“) sąsają. Įprastoms programoms to neturėtų prireikti."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"susaistyti su VPN paslauga"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Leidžiama programai valdyti tinklo politiką ir apibrėžti konkrečios programos taisykles."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"keisti tinklo naudojimo apskaitą"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Leidžiama programai keisti, kaip tinklas naudojamas, palyginti su programomis. Neskirta naudoti įprastoms programoms."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"pasiekti pranešimus"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Programai leidžiama gauti, patikrinti ir išvalyti pranešimus, įskaitant pranešimus, kuriuos paskelbė kitos programos."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Nustatyti slaptažodžio taisykles"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Valdyti leidžiamą ekrano atrakinimo slaptažodžių ilgį ir leidžiamus naudoti simbolius."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Stebėti bandymus atrakinti ekraną"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Šablonas išvalytas"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Pridėtas langelis"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Šablonas užbaigtas"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %2$d valdiklis iš %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Pridėti valdiklį."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tuščia"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Atrakinimo sritis išplėsta."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index d563a05..646b7fa 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Krātuve"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Piekļūst USB krātuvei."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Piekļūstiet SD kartei."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Pieejamības funkcijas"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkcijas, kuras palīgtehnoloģija var pieprasīt."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"atspējot vai pārveidot statusa joslu"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Ļauj lietotnei atspējot statusa joslu vai pievienot un noņemt sistēmas ikonas."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"statusa josla"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ļauj lietotnei lasīt ierīcē saņemtos šūnu apraides ziņojumus. Šūnu apraides brīdinājumi tiek piegādāti dažās atrašanās vietās, lai brīdinātu jūs par ārkārtas situācijām. Ļaunprātīgas lietotnes var traucēt ierīces veiktspēju vai darbības, kad ir saņemts ārkārtas šūnas apraides ziņojums."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"sūtīt īsziņas"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Ļauj lietotnei sūtīt īsziņas. Tas var radīt neparedzētas izmaksas. Ļaunprātīgas lietotnes var radīt jums izmaksas, sūtot ziņojumus bez jūsu apstiprinājuma."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"īsziņu sūtīšana bez apstiprinājuma"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Ļauj lietotnei sūtīt īsziņas. Tas var radīt neparedzētas izmaksas. Ļaunprātīgas lietotnes var radīt jums izmaksas, sūtot ziņojumus bez jūsu apstiprinājuma."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"nosūtīt “atbildēt ziņojumā” notikumus"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Ļauj lietotnei nosūtīt pieprasījumus citām ziņojumapmaiņas lietotnēm par “atbildēt ziņojumā” notikumu apstrādi ienākošajiem zvaniem."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lasīt ziņojumus (SMS vai MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Ļauj lietotnei lasīt planšetdatorā vai SIM kartē saglabātās īsziņas. Tas ļauj lietotnei lasīt visas īsziņas, neraugoties uz to saturu vai konfidencialitāti."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Ļauj lietotnei lasīt tālrunī vai SIM kartē saglabātās īsziņas. Tas ļauj lietotnei lasīt visas īsziņas, neraugoties uz to saturu vai konfidencialitāti."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Liek darbību pārvaldniekam pāriet izslēgšanas stāvoklī. Neveic pilnīgu izslēgšanu."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"novērst lietojumprogrammu pārslēgšanu"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Novērš lietotāja pārslēgšanos uz citu lietotni."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pašreizējās lietotnes informācijas iegūšana"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Ļauj īpašniekam izgūt privātu informāciju par pašreizējo lietojumprogrammu ekrāna priekšplānā."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"pārraudzīt un kontrolēt visu lietotņu atvēršanu"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Ļauj lietotnei pārraudzīt un kontrolēt, kā sistēmā tiek palaistas darbības. Ļaunprātīgas lietotnes var pilnībā uzlauzt sistēmu. Šī atļauja ir nepieciešama tikai izstrādei, taču ne parastai lietošanai."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"sūtīt apraidi par pakotnes noņemšanu"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Ļauj lietojumprogrammai lasīt pašreizējos zema akumulatora enerģijas patēriņa datus. Var atļaut lietojumprogrammai iegūt detalizētu informāciju par to, kuras lietotnes izmantojat."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"akumulatora statistikas pārveidošana"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Ļauj lietotnei pārveidot apkopoto statistiku par akumulatoru. Atļauja neattiecas uz parastām lietotnēm."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"Izgūt lietotnes darbību statistiku"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Ļauj lietotnei izgūt apkopoto statistiku par lietojumprogrammas darbību. Atļauja neattiecas uz parastām lietotnēm."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"Pārveidot lietotnes darbības statistiku"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Ļauj lietotnei pārveidot apkopoto statistiku par lietojumprogrammas darbību. Atļauja neattiecas uz parastām lietotnēm."</string> <string name="permlab_backup" msgid="470013022865453920">"kontrolēt sistēmas dublējumu un atjaunošanu"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Ļauj lietotnei kontrolēt sistēmas dublēšanas un atjaunošanas mehānismu. Atļauja neattiecas uz parastām lietotnēm."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Apstiprināt pilnu dublējumu vai atjaunot darbību"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Ļauj īpašniekam izveidot saiti ar ievades metodes augstākā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"saistīt ar pieejamības pakalpojumu"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Ļauj īpašniekam izveidot saiti ar pieejamības pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm šī atļauja nav nepieciešama."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"pieprasīt funkciju “Pārlūkot pieskaroties”"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Ļauj pieprasīt mijiedarbības režīmu. Izmantojot šo režīmu, vienumi, kuriem pieskaraties, tiek izrunāti skaļi, un lietotāja saskarni var pārlūkot ar žestiem."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"pieprasīt uzlabotu tīmekļa pieejamību"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Ļauj pieprasīt tīmekļa pieejamības uzlabojumu iespējošanu, piemēram, ļauj instalēt skriptus, lai padarītu lietotnes saturu pieejamāku."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"saistīt ar īsziņu pakalpojumu"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Ļauj īpašniekam veikt saistīšanu ar īsziņu pakalpojuma augstākā līmeņa saskarni (piem., SpellCheckerService). Parastajām lietotnēm tas nekad nav nepieciešams."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"saistīt ar VPN pakalpojumu"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Ļauj lietotnei pārvaldīt tīkla politikas un noteikt lietotnes kārtulas."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Tīkla lietojuma uzskaites mainīšana"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ļauj lietotnei mainīt to, kā tīkla lietojums tiek uzskaitīts saistībā ar lietotnēm. Atļauja neattiecas uz parastām lietotnēm."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"piekļuve paziņojumiem"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Ļauj lietotnei izgūt, pārbaudīt un dzēst paziņojumus, tostarp lietotņu publicētos paziņojumus."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Paroles kārtulu iestatīšana"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolē ekrāna atbloķēšanas parolē atļautās rakstzīmes un garumu."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Kombinācija notīrīta"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Šūna pievienota"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Kombinācija pabeigta"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. %2$d. logrīks no %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Pievienot logrīku."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tukšs"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Atbloķēšanas apgabals ir izvērsts."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index ead43f1..784a424 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Storan"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Akses storan USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Akses kad SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Ciri kebolehaksesan"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Ciri yang boleh diminta oleh teknologi bantuan."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"lumpuhkan atau ubah suai bar status"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"bar status"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Membolehkan apl membaca mesej siaran sel yang diterima oleh peranti anda. Isyarat siaran sel dihantar di beberapa lokasi untuk memberi amaran kepada anda tentang situasi kecemasan. Apl hasad boleh mengganggu prestasi atau operasi peranti anda apabila siaran sel kecemasan diterima."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"hantar mesej SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Membenarkan apl menghantar mesej SMS. Ini boleh menyebabkan caj di luar jangkaan. Apl hasad boleh membuat anda kerugian wang dengan menghantar mesej tanpa pengesahan anda."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"hantar mesej SMS tanpa pengesahan"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Membenarkan apl menghantar mesej SMS. Ini boleh menyebabkan caj di luar jangkaan. Apl hasad boleh membuat anda kerugian wang dengan menghantar mesej tanpa pengesahan anda."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"acara hantar respons-melalui-mesej"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Membenarkan apl menghantar permintaan kepada apl permesejan lain untuk mengendalikan acara respons-melalui-mesej untuk panggilan masuk."</string> <string name="permlab_readSms" msgid="8745086572213270480">"baca mesej teks anda (SMS atau MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Membenarkan apl membaca mesej SMS yang tersimpan pada tablet atau kad SIM anda. Ini membenarkan apl membaca semua mesej SMS, tanpa mengira kandungan atau kerahsiaan."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Membenarkan apl membaca mesej SMS yang tersimpan pada telefon atau kad SIM anda. Ini membenarkan apl membaca semua mesej SMS, tanpa mengira kandungan atau kerahsiaan."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Meletakkan pengurus aktiviti dalam keadaan tutup. Tidak melaksanakan penutupan lengkap."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"halang pertukaran apl"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Menghalang pengguna daripada bertukar kepada apl lain."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"dapatkan maklumat apl semasa"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Membenarkan pemegang mendapatkan maklumat peribadi tentang permohonan semasa di latar hadapan skrin"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"pantau dan kawal semua pelancaran apl"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Membenarkan apl untuk memantau dan mengawal cara sistem melancarkan aktiviti. Apl hasad boleh menjejaskan sistem sepenuhnya. Kebenaran ini hanya diperlukan untuk pembangunan, tidak sekali-kali untuk penggunaan biasa."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"hantar siaran bahawa pakej telah dialih keluar"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Membenarkan aplikasi membaca data penggunaan bateri tahap rendah semasa. Boleh membenarkan aplikasi untuk mencari maklumat terperinci tentang apl yang anda gunakan."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"ubah suai statistik bateri"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Membenarkan apl mengubah suai statistik bateri yang dikumpul. Bukan untuk penggunaan apl normal."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"dapatkan semula statistik pengendalian apl"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Membenarkan apl mendapatkan semula statistik pengendalian aplikasi yang dikumpul. Bukan untuk kegunaan apl biasa."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"ubah suai apl ops statistik"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Membenarkan apl mengubah suai statistik operasi aplikasi yang dikumpul. Bukan untuk kegunaan apl biasa."</string> <string name="permlab_backup" msgid="470013022865453920">"sandaran dan pemulihan sistem kawalan"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Membenarkan apl untuk mengawal sandaran sistem dan memulihkan mekanisme. Bukan untuk digunakan oleh apl biasa."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"sahkan penyandaran penuh atau pemulihan operasi"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi kaedah input itu. Tidak sekali-kali diperlukan untuk apl biasa."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"terikat kepada perkhidmatan yang boleh diakses"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan yang boleh diakses. Tidak sekali-kali diperlukan untuk apl biasa."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"minta jelajah melalui sentuhan"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Membenarkan pengguna untuk meminta mod interaksi agar item yang disentuh disebut dengan kuat dan UI boleh dijelajahi melalui gerak isyarat."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"minta kebolehaksesan web dipertingkatkan"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Membenarkan pengguna untuk meminta mendayakan penambahbaikan kebolehaksesan web. Sebagai contoh, memasang skrip untuk menjadikan kandungan apl lebih mudah diakses."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"terikat kepada perkhidmatan teks"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Membenarkan pemegang mengikat kepada antara muka peringkat atasan perkhidmatan teks(mis. PerkhidmatanPenyemakEjaan). Tidak seharusnya diperlukan untuk apl biasa."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"terikat kepada perkhidmatan VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Membenarkan apl mengurus dasar rangkaian dan menentukan peraturan khusus apl."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ubah suai perakaunan penggunaan rangkaian"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Membenarkan apl untuk mengubah suai bagaimana penggunaan rangkaian diambil kira terhadap apl. Bukan untuk digunakan oleh apl biasa."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"pemberitahuan akses"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Membenarkan apl untuk mendapatkan semula, memeriksa dan memadam bersih pemberitahuan, termasuk yang disiarkan oleh apl lain."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Tetapkan peraturan kata laluan"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan buka kunci skrin."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Memantau percubaan buka kunci skrin"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Corak dipadamkan"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Sel ditambahkan"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Corak siap"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d dari %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Tambah widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Kosong"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Bahagian buka kunci dikembangkan."</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index b715c9f..485a11f 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Tilgang til USB-lagring."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Tilgang til minnekortet."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Tilgjengelighetsfunksjoner"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funksjoner støttende teknologi kan be om."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"deaktivere eller endre statusfeltet"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"statusrad"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillater at appen kan lese kringkastede meldinger enheten din mottar. Kringkastede varsler leveres noen steder for å advare deg om nødsituasjoner. Skadelige apper kan forstyrre ytelsen eller funksjonen til enheten din når en kringkastet nødmelding mottas."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"sende SMS-meldinger"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Lar appen sende tekstmeldinger. Dette kan resultere i uventede kostnader. Merk at skadelige apper kan påføre deg kostnader ved å sende meldinger uten bekreftelse fra deg."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"send tekstmeldinger uten godkjenning"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Lar appen sende tekstmeldinger. Dette kan resultere i uventede kostnader. Merk at skadelige apper kan påføre deg kostnader ved å sende meldinger uten bekreftelse fra deg."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send svar via melding-hendelser"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Lar appen sende forespørsler om håndtering av svar via melding-hendelser for innkommende anrop til andre meldingsapper."</string> <string name="permlab_readSms" msgid="8745086572213270480">"leser tekstmeldinger (SMS eller MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Lar appen lese tekstmeldinger lagret på nettbrettet eller SIM-kortet ditt. Dette lar appen lese alle tekstmeldingene dine, uavhengig av innhold og konfidensialitet."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Lar appen lese tekstmeldinger lagret på telefonen eller SIM-kortet ditt. Dette lar appen lese alle tekstmeldingene dine, uavhengig av innhold og konfidensialitet."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Lar applikasjonen sette aktivitetshåndtereren i avslutningstilstand. Slår ikke systemet helt av."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"forhindre applikasjonsbytte"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hindrer brukeren i å bytte til en annen app."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"få informasjon om appen"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Tillater brukeren å få tilgang til privat informasjon om den aktuelle appen i forgrunnen på skjermen."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"avervåke og kontrollere all oppstart av apper"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Lar appen overvåke og kontrollere hvordan systemet starter opp aktiviteter. Ondsinnede apper kan utsette hele systemet for sikkerhetsbrudd. Denne tillatelsen er bare nødvendig for utviklere, aldri for vanlig bruk."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"kringkaste melding om fjernet pakke"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Lar apper lese gjeldende data på lavt nivå om batteribruk. Kan også la appen finne ut detaljert informasjon om hvilke apper du bruker."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"endre batteristatistikk"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Lar appen endre innsamlet batteristatistikk. Ikke beregnet på vanlige apper."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hente bruksstatistikk for appen"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Lar appen hente innsamlet bruksstatistikk. Ikke beregnet på vanlige apper."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"endre bruksstatistikk for appen"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Lar appen endre innsamlet bruksstatistikk. Ikke beregnet på vanlige apper."</string> <string name="permlab_backup" msgid="470013022865453920">"kontrollere sikkerhetskopiering og gjenoppretting"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Lar appen kontrollere systemets mekanisme for sikkerhetskopiering og gjenoppretting. Ikke beregnet på vanlige apper."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"bekrefte en fullstendig sikkerhetskopi, eller gjenopprette driften"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lar innehaveren binde det øverste nivået av grensesnittet til en inndatametode. Skal aldri være nødvendig for normale apper."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"binde seg til en tilgjengelighetstjeneste"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Gir innehaveren tillatelse til å bindes til det øverste nivået av grensesnittet for en tilgjengelighetstjeneste. Skal aldri være nødvendig for vanlige apper."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"be om utforsking ved berøring"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Tillater brukeren å be om en interaksjonsmodus der berørte elementer sies høyt, og brukergrensesnittet kan utforskes med bevegelser."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"be om forbedret nettilgjengelighet"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Tillater brukeren å be om aktivering av forbedret nettilgjengelighet. Dette kan for eksempel være installasjon av skript for å gjøre appinnhold mer tilgjengelig."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"binde til en teksttjeneste"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Lar innehaveren binde seg til øverste grensesnittnivå for en teksttjeneste (f.eks. SpellCheckerService). Skal aldri være nødvendig for vanlige apper."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"binde deg til en VPN-tjeneste"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Lar appen administrere retningslinjene for nettverket og definere appspesifikke regler."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Modifisering av regnskapsføring av nettverksbruk"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Lar appen endre hvordan nettverksbruk regnes ut for apper. Ikke beregnet på vanlige apper."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"varseltilgang"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Lar appen hente, gjennomgå og fjerne varsler, inkludert de som sendes fra andre apper."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Angi passordregler"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller tillatt lengde og tillatte tegn i passord for opplåsing av skjerm."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Overvåk forsøk på opplåsing av skjerm"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Mønsteret er slettet"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Celle er lagt til"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Mønsteret er fullført"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Modul %2$d av %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Legg til modul."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tom"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Opplåsingsfeltet vises."</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 0f0e3d9..95da93b 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Opslagruimte"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Toegang krijgen tot USB-opslag."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Toegang tot de SD-kaart."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Toegankelijkheidsfuncties"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Functies die kunnen worden aangevraagd door ondersteunende technologie."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"statusbalk uitschakelen of wijzigen"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Hiermee kan de app de statusbalk uitschakelen of systeempictogrammen toevoegen en verwijderen."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"statusbalk"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Toestaan dat de app infodienstberichten leest die worden ontvangen op uw apparaat. Infodienstberichten worden verzonden naar bepaalde locaties om u te waarschuwen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van uw apparaat verstoren wanneer een infodienstbericht wordt ontvangen."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMS-berichten verzenden"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Hiermee kan de app sms-berichten verzenden. Dit kan tot onverwachte kosten leiden. Schadelijke apps kunnen u geld kosten doordat ze zonder uw bevestiging berichten kunnen verzenden."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"zonder toestemming sms\'jes verzenden"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Hiermee kan de app sms-berichten verzenden. Dit kan tot onverwachte kosten leiden. Schadelijke apps kunnen u geld kosten doordat ze zonder uw bevestiging berichten kunnen verzenden."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"gebeurtenissen voor reageren-via-berichten verzenden"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Hiermee kan de app verzoeken verzenden aan andere bericht-apps om gebeurtenissen voor reageren-via-berichten voor inkomende oproepen te verwerken."</string> <string name="permlab_readSms" msgid="8745086572213270480">"uw tekstberichten (SMS of MMS) lezen"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op uw tablet of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op uw telefoon of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Hiermee wordt activiteitenbeheer uitgeschakeld. Er wordt geen volledige uitschakeling uitgevoerd."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"schakelen tussen apps voorkomen"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hiermee wordt voorkomen dat de gebruiker overschakelt naar een andere app."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"huidige appgegevens ophalen"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"De houder kan hiermee persoonlijke gegevens ophalen over de applicatie die momenteel op de voorgrond wordt weergegeven."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"alle startende apps bijhouden en beheren"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Hiermee kan de app de manier bijhouden en beheren waarop het systeem activiteiten start. Schadelijke apps kunnen het systeem volledig in gevaar brengen. Deze machtiging is alleen voor ontwikkeling vereist, nooit voor normaal gebruik."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"melding verzenden dat pakket is verwijderd"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Hiermee kan de app het huidige accugebruik voor gegevens op laag niveau lezen. Een app kan hierdoor mogelijk gedetailleerde informatie achterhalen over de door u gebruikte apps."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"accustatistieken aanpassen"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Hiermee kan de app verzamelde accustatistieken wijzigen. Niet voor gebruik door normale apps."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"bewerkingsstatistieken van apps ophalen"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Hiermee kan de app verzamelde bewerkingsstatistieken van apps ophalen. Niet voor gebruik door normale apps."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"bewerkingsstatistieken van apps wijzigen"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Hiermee kan de app verzamelde bewerkingsstatistieken van apps wijzigen. Niet voor gebruik door normale apps."</string> <string name="permlab_backup" msgid="470013022865453920">"systeemback-up en -herstel beheren"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Hiermee kan de app het beheer van het mechanisme voor systeemback-up en -herstel beheren. Niet voor gebruik door normale apps."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"een volledige back-up- of herstelbewerking bevestigen"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Hiermee kan de houder zich verbinden met de hoofdinterface van een invoermethode. Nooit vereist voor normale apps."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"koppelen aan een toegankelijkheidsservice"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Hiermee wordt de houder toegestaan verbinding te maken met de hoofdinterface van een toegankelijkheidsservice. Nooit vereist voor normale apps."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"verkennen via aanraking aanvragen"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Hiermee kan de houder een interactiemodus aanvragen waarin aangeraakte items worden uitgesproken en de gebruikersinterface kan worden bediend met gebaren."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"verbeterde internettoegankelijkheid aanvragen"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Hiermee kan de houder het inschakelen van verbeteringen voor internettoegankelijkheid aanvragen. Bijvoorbeeld scripts installeren om app-inhoud toegankelijker te maken."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"koppelen aan een sms-service"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Hiermee kan de gebruiker koppelen met de hoofdinterface van een tekstservice (zoals SpellCheckerService). Dit is niet nodig voor normale apps."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"koppelen aan een VPN-service"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Hiermee kan de app het netwerkbeleid beheren en app-specifieke regels definiëren."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verrekening van netwerkgebruik aanpassen"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Hiermee kan een app aanpassen hoe het netwerkgebruik wordt toegekend aan apps. Dit wordt niet gebruikt door normale apps."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"toegang tot meldingen"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Hiermee kan de app meldingen ophalen, onderzoeken en wissen, waaronder meldingen die zijn verzonden door andere apps."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Wachtwoordregels instellen"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"De lengte en tekens beheren die zijn toegestaan in wachtwoorden voor schermontgrendeling."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Pogingen voor schermontgrendeling bijhouden"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Patroon gewist"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Cel toegevoegd"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Patroon voltooid"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d van %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Widget toevoegen."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Leeg"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Ontgrendelingsgebied uitgevouwen."</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 417ee78..f15c106 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Pamięć"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Dostęp do nośnika USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Dostęp do karty SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funkcje ułatwień dostępu"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkcje, których może zażądać technologia ułatwień dostępu."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"wyłączanie lub zmienianie paska stanu"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"pasek stanu"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Zezwala aplikacji na odczyt wiadomości z sieci komórkowej odebranych na urządzeniu. Alerty sieci komórkowej są dostarczane w niektórych lokalizacjach w celu ostrzeżenia Cię o sytuacjach alarmowych. Złośliwe aplikacje mogą wpływać na wydajność lub zakłócać działanie urządzenia po odebraniu wiadomości alarmowej z sieci komórkowej."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"wysyłanie wiadomości SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Pozwala aplikacji na wysyłanie SMS-ów. Może to skutkować nieoczekiwanymi opłatami. Złośliwe aplikacje mogą generować koszty, wysyłając wiadomości bez Twojego potwierdzenia."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"wysyłanie wiadomości SMS bez potwierdzenia"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Pozwala aplikacji na wysyłanie SMS-ów. Może to skutkować nieoczekiwanymi opłatami. Złośliwe aplikacje mogą generować koszty, wysyłając wiadomości bez Twojego potwierdzenia."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"wysyłanie zdarzeń odpowiedzi przez SMS"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Zezwala aplikacjom na wysyłanie żądań do innych aplikacji komunikacyjnych w celu obsługi zdarzeń odpowiedzi przez SMS dla połączeń przychodzących."</string> <string name="permlab_readSms" msgid="8745086572213270480">"odczytywanie wiadomości tekstowych (SMS i MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Pozwala aplikacji na odczyt SMS-ów zapisanych na tablecie lub na karcie SIM. Aplikacja z tym uprawnieniem może czytać wszystkie SMS-y niezależnie od ich treści lub poufności."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Pozwala aplikacji na odczyt SMS-ów zapisanych na telefonie lub na karcie SIM. Aplikacja z tym uprawnieniem może czytać wszystkie SMS-y niezależnie od ich treści lub poufności."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Przełącza menedżera aktywności w stan wyłączenia. Nie wykonuje pełnego wyłączenia."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zapobieganie przełączaniu aplikacji"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Uniemożliwia użytkownikowi przełączenie na inną aplikację."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pobierz informacje o bieżącej aplikacji"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Zezwala posiadaczowi na pobieranie prywatnych informacji o bieżącej aplikacji i wyświetlanie ich na pierwszym planie ekranu."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorowanie i kontrolowanie wszystkich uruchamianych aplikacji"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Pozwala aplikacji na monitorowanie i kontrolowanie sposobu uruchamiania działań przez system. Złośliwe aplikacje mogą całkowicie naruszyć zabezpieczenia systemu. To uprawnienie nigdy nie jest potrzebne podczas normalnego użytkowania, a jedynie podczas programowania."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"wysyłanie transmisji informującej o usuniętym pakiecie"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Zezwala aplikacji na odczytywanie bieżących danych niskiego poziomu o wykorzystaniu baterii. Możliwe jest wtedy zbieranie przez aplikację szczegółowych danych o używanych aplikacjach."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"zmienianie statystyk dotyczących baterii"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Zezwala aplikacji na modyfikowanie zebranych statystyk dotyczących baterii. Nieprzeznaczone dla zwykłych aplikacji."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"pobieranie statystyk działania aplikacji"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Zezwala aplikacji na pobieranie zebranych statystyk działania aplikacji. Nieprzeznaczone dla zwykłych aplikacji."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modyfikowanie statystyk działania aplikacji"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Zezwala aplikacji na modyfikowanie zebranych statystyk działania aplikacji. Nieprzeznaczone dla zwykłych aplikacji."</string> <string name="permlab_backup" msgid="470013022865453920">"kontrolowanie tworzenia i przywracania kopii zapasowych systemu"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Pozwala aplikacji na sterowanie mechanizmem tworzenia kopii zapasowych systemu i ich przywracania. Nieprzeznaczone dla zwykłych aplikacji."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Potwierdzenie operacji utworzenia pełnej kopii zapasowej lub przywracania"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Pozwala na powiązanie wybranego sposobu wprowadzania tekstu z interfejsem najwyższego poziomu. To uprawnienie nie powinno być nigdy wymagane przez zwykłe aplikacje."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"tworzenie powiązania z usługą ułatwień dostępu"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi ułatwień dostępu. Nieprzeznaczone dla zwykłych aplikacji."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"żądanie dotyczące czytania dotykiem"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Pozwala właścicielowi zażądać włączenia trybu interaktywnego, w którym nazwy klikniętych elementów są wypowiadane na głos, a po interfejsie można poruszać się gestami."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"żądanie dotyczące dodatkowych ułatwień dostępu w internecie"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Pozwala właścicielowi zażądać włączenia internetowych funkcji ułatwień dostępu. Może to być np. zainstalowanie skryptów, które sprawią, że treść aplikacji będzie łatwiej dostępna."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"tworzenie powiązania z usługą tekstową"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi tekstowej (np. SpellCheckerService). Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"tworzenie powiązania z usługą VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Pozwala aplikacji na zarządzanie zasadami dotyczącymi sieci i definiowanie reguł aplikacji."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modyfikowanie sposobu naliczania użycia sieci"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Pozwala aplikacji na zmienianie sposobu rozliczania wykorzystania sieci przez aplikacje. Nieprzeznaczone dla zwykłych aplikacji."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"dostęp do powiadomień"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Umożliwia aplikacji pobieranie, sprawdzanie i usuwanie powiadomień, także tych, które pochodzą z innych aplikacji."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Określ reguły hasła"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolowanie długości haseł odblokowania ekranu i dozwolonych w nich znaków"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitoruj próby odblokowania ekranu"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Wzór wyczyszczony"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Dodano komórkę."</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Wzór ukończony"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widżet %2$d z %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Dodaj widżet."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Puste"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Rozwinięto obszar odblokowania."</string> @@ -1039,7 +1054,7 @@ <string name="loading" msgid="7933681260296021180">"Wczytywanie…"</string> <string name="capital_on" msgid="1544682755514494298">"Wł"</string> <string name="capital_off" msgid="6815870386972805832">"Wył"</string> - <string name="whichApplication" msgid="4533185947064773386">"Zakończ czynność przez..."</string> + <string name="whichApplication" msgid="4533185947064773386">"Wykonaj czynność przez..."</string> <string name="alwaysUse" msgid="4583018368000610438">"Domyślne dla tej czynności"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Wyczyść wartości domyślne w: Ustawienia systemu > Aplikacje > Pobrane."</string> <string name="chooseActivity" msgid="7486876147751803333">"Wybierz czynność"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index ba97b8f..7f7d6d2 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Aceder ao armazenamento USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Aceder ao cartão SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funcionalidades de acessibilidade"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funcionalidades que a tecnologia de apoio pode solicitar."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar barra de estado"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite à aplicação desativar a barra de estado ou adicionar e remover ícones do sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de estado"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que a aplicação leia mensagens de transmissão celular recebidas pelo seu dispositivo. Os alertas de transmissão celular são fornecidos em algumas localizações para avisá-lo sobre situações de emergência. As aplicações maliciosas podem interferir com o desempenho ou funcionamento do seu dispositivo quando for recebida uma transmissão celular de emergência."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"enviar mensagens SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que a aplicação envie mensagens SMS. Isto pode resultar em custos inesperados. As aplicações maliciosas podem fazer com que incorra em custos, enviando mensagens sem a sua confirmação."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"enviar mensagens SMS sem confirmação"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permite que a aplicação envie mensagens SMS. Isto pode resultar em custos inesperados. As aplicações maliciosas podem fazer com que incorra em custos, enviando mensagens sem a sua confirmação."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"enviar eventos de resposta-via-mensagem"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permite à aplicação enviar pedidos a outras aplicações de mensagens para processar eventos de resposta-via-mensagem para as chamadas recebidas."</string> <string name="permlab_readSms" msgid="8745086572213270480">"ler as mensagens de texto (SMS ou MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que a aplicação leia mensagens SMS guardadas no tablet ou no cartão SIM. Permite que a aplicação leia todas as mensagens SMS, independentemente do conteúdo ou da confidencialidade das mesmas."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permite que a aplicação leia mensagens SMS guardadas no telemóvel ou no cartão SIM. Permite que a aplicação leia todas as mensagens SMS, independentemente do conteúdo ou da confidencialidade das mesmas."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Coloca o gestor de actividade num estado de encerramento. Não executa um encerramento completo."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir trocas de aplicações"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impede que o utilizador mude para outra aplicação."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obter informações da aplicação atual"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite ao titular recuperar informações privadas acerca da aplicação atual no primeiro plano do ecrã."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorizar e controlar a iniciação de todas as aplicações"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que uma aplicação monitorize e controle a forma como o sistema inicia atividades. As aplicações maliciosas podem comprometer totalmente o sistema. Esta autorização só é necessária para programação, nunca para utilização normal."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar difusão de pacote removido"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite que uma aplicação leia os atuais dados de utilização da bateria de baixo nível. Poderá permitir que a aplicação encontre informações detalhadas sobre as aplicações que utiliza."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar estatísticas da bateria"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite que a aplicação modifique as estatísticas recolhidas sobre a bateria. Não se destina a utilização por aplicações normais."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"obter estatísticas de utilização da aplicação"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permite que a aplicação obtenha as estatísticas de utilização da aplicação recolhidas. Não se destina a utilização por aplicações normais."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estatísticas de utilização da aplicação"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que a aplicação modifique as estatísticas de utilização de aplicação recolhidas. Não se destina a utilização por aplicações normais."</string> <string name="permlab_backup" msgid="470013022865453920">"controlar a cópia de segurança e restauro do sistema"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permite que a aplicação controle o mecanismo de cópia de segurança e de restauro do sistema. Não se destina a utilização por aplicações normais."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar uma operação de restauro ou de cópia de segurança completa"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite ao titular vincular-se à interface de nível superior de um método de entrada. Nunca deve ser necessário para aplicações normais."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vincular a um serviço de acessibilidade"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite que o titular vincule a interface de nível superior de um serviço de acessibilidade. Nunca deverá ser necessário para aplicações normais."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"solicitar exploração por toque"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Permite ao Hoder solicitar um modo de interação em que os itens tocados são falados em voz alta e a IU pode ser explorada através de gestos."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"solicitar acessibilidade Web melhorada"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Permite ao Hoder solicitar a ativação de melhoramentos de acessibilidade Web. Por exemplo, a instalação de scripts do Google para tornar o conteúdo das aplicações mais acessível."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"vincular a um serviço de texto"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite ao titular ligar-se à interface de nível superior de um serviço de texto (por exemplo SpellCheckerService). Nunca deverá ser necessário para aplicações normais."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"vincular a um serviço VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que a aplicação faça a gestão de políticas de rede e defina regras específicas de aplicações."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificar contabilização da utilização da rede"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que a aplicação modifique o modo como a utilização da rede é contabilizada em relação a aplicações. Nunca é necessário para aplicações normais."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"aceder às notificações"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que a aplicação obtenha, examine e limpe notificações, incluindo as que foram publicadas por outras aplicações."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras de palavra-passe"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar o comprimento e os caracteres permitidos nas palavras-passe de desbloqueio do ecrã."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizar tentativas de desbloqueio do ecrã"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Sequência apagada"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Célula adicionada"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Sequência concluída"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adicionar widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vazio"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área de desbloqueio expandida."</string> @@ -1039,7 +1054,7 @@ <string name="loading" msgid="7933681260296021180">"A carregar…"</string> <string name="capital_on" msgid="1544682755514494298">"Activado"</string> <string name="capital_off" msgid="6815870386972805832">"Desactivar"</string> - <string name="whichApplication" msgid="4533185947064773386">"Concluir acção utilizando"</string> + <string name="whichApplication" msgid="4533185947064773386">"Concluir ação utilizando"</string> <string name="alwaysUse" msgid="4583018368000610438">"Utilizar por predefinição para esta acção."</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Limpar a predefinição nas Definições do Sistema > Aplicações > Transferidas."</string> <string name="chooseActivity" msgid="7486876147751803333">"Escolha uma ação"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 5dfbe05..a2c6675 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Acessa o armazenamento USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Acessar o cartão SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Recursos de acessibilidade"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Recursos que a tecnologia assistencial pode solicitar."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o aplicativo desative a barra de status ou adicione e remova ícones do sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"barra de status"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o aplicativo leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Aplicativos maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"enviar mensagens SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o aplicativo envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Aplicativos maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"enviar mensagens SMS sem confirmação"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permite que o aplicativo envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Aplicativos maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"enviar eventos de resposta por mensagem"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permite que o aplicativo envie solicitações a outros aplicativos de mensagens para processar eventos de resposta por mensagem para chamadas recebidas."</string> <string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o aplicativo leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o aplicativo leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permite que o aplicativo leia mensagens SMS armazenadas no telefone ou cartão SIM. Isso permite que o aplicativo leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Coloca o gerenciador de atividades em um estado de desligamento. Não executa o desligamento completo."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"evitar trocas de aplicativo"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impede que o usuário alterne para outro aplicativo."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obter informações do aplicativo atual"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite ao titular recuperar informações particulares sobre o aplicativo atual em primeiro plano na tela."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorar e controlar todos os aplicativos que estão sendo iniciados"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que o aplicativo monitore e controle a forma como o sistema inicia atividades. Aplicativos maliciosos podem comprometer completamente o sistema. Esta permissão só é necessária para o desenvolvimento, nunca para o uso normal."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar transmissão removida do pacote"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite que o aplicativo leia os dados de uso da bateria de baixo nível atuais. Pode fornecer ao aplicativo informações detalhadas sobre os aplicativos usados por você."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar estatísticas da bateria"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite que o aplicativo modifique as estatísticas coletadas da bateria. Não deve ser usado em aplicativos normais."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"recuperar estatísticas de operações de aplicativos"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permite que o aplicativo recupere as estatísticas de operações de aplicativos. Não deve ser usado em aplicativos normais."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estatísticas de operações de aplicativos"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que o aplicativo modifique as estatísticas de operações de aplicativos. Não deve ser usado em aplicativos normais."</string> <string name="permlab_backup" msgid="470013022865453920">"controlar backup e restauração do sistema"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permite que o aplicativo controle o backup do sistema e restaure mecanismos. Não deve ser usado em aplicativos normais."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar um backup completo ou uma operação de restauração"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite que o proprietário utilize a interface de nível superior de um método de entrada. Nunca deve ser necessário para aplicativos normais."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"usar um serviço de acessibilidade"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite que o proprietário use a interface de nível superior de um serviço de acessibilidade. Nunca deve ser necessário para aplicativos comuns."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"solicitar explorar por toque"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Permite ao proprietário solicitar um modo de interação em que os itens tocados são falados em voz alta e a interface do usuário pode ser explorada com gestos."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"solicitar acessibilidade melhorada da Web"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Permite ao proprietário solicitar melhorias na acessibilidade da Web. Por exemplo, a instalação de scripts para tornar o conteúdo de aplicativos mais acessível."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"sujeitar-se a um serviço de texto"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite que o proprietário utilize interface de nível superior de um serviço de texto (por exemplo, SpellCheckerService). Nunca deve ser necessário para aplicativos normais."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"se ligam a um serviço de VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que o aplicativo gerencie políticas de rede e definia regras específicas para o aplicativo."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificar contagem de uso da rede"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que o aplicativo modifique como o uso da rede é contabilizado em relação aos aplicativos. Não deve ser usado em aplicativos normais."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"acessar notificações"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que o aplicativo recupere, examine e limpe notificações, inclusive as postadas por outros aplicativos."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras para senha"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controle o tamanho e os caracteres permitidos nas senhas de desbloqueio de tela."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitorar tentativas de desbloqueio da tela"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Padrão apagado"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Célula adicionada"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Padrão concluído"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adicionar widget"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Vazio"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Área de desbloqueio expandida."</string> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index 799b9a2..53729f7 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -310,6 +310,10 @@ <!-- no translation found for permgroupdesc_storage (7442318502446874999) --> <skip /> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Access a la carta SD."</string> + <!-- no translation found for permgrouplab_accessibilityFeatures (7919025602283593907) --> + <skip /> + <!-- no translation found for permgroupdesc_accessibilityFeatures (4205196881678144335) --> + <skip /> <string name="permlab_statusBar" msgid="7417192629601890791">"deactivar u modifitgar la trav da status"</string> <!-- no translation found for permdesc_statusBar (8434669549504290975) --> <skip /> @@ -342,9 +346,9 @@ <string name="permlab_sendSms" msgid="5600830612147671529">"trametter messadis SMS"</string> <!-- no translation found for permdesc_sendSms (7094729298204937667) --> <skip /> - <!-- no translation found for permlab_sendSmsNoConfirmation (4781483105951730228) --> + <!-- no translation found for permlab_sendRespondViaMessageRequest (8713889105305943200) --> <skip /> - <!-- no translation found for permdesc_sendSmsNoConfirmation (402569800862935907) --> + <!-- no translation found for permdesc_sendRespondViaMessageRequest (7107648548468778734) --> <skip /> <!-- no translation found for permlab_readSms (8745086572213270480) --> <skip /> @@ -449,6 +453,10 @@ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"evitar il midar tranter applicaziuns"</string> <!-- no translation found for permdesc_stopAppSwitches (8262195802582255021) --> <skip /> + <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) --> + <skip /> + <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) --> + <skip /> <!-- no translation found for permlab_runSetActivityWatcher (892239094867182656) --> <skip /> <!-- no translation found for permdesc_runSetActivityWatcher (6003603162578577406) --> @@ -477,6 +485,14 @@ <skip /> <!-- no translation found for permdesc_updateBatteryStats (6862817857178025002) --> <skip /> + <!-- no translation found for permlab_getAppOpsStats (1508779687436585744) --> + <skip /> + <!-- no translation found for permdesc_getAppOpsStats (6243887041577912877) --> + <skip /> + <!-- no translation found for permlab_updateAppOpsStats (8829097373851521505) --> + <skip /> + <!-- no translation found for permdesc_updateAppOpsStats (50784596594403483) --> + <skip /> <string name="permlab_backup" msgid="470013022865453920">"controllar las copias da segirezza e la restauraziun dal sistem"</string> <!-- no translation found for permdesc_backup (6912230525140589891) --> <skip /> @@ -517,6 +533,14 @@ <skip /> <!-- no translation found for permdesc_bindAccessibilityService (7034615928609331368) --> <skip /> + <!-- no translation found for permlab_canRequestTouchExplorationMode (6094034289937541846) --> + <skip /> + <!-- no translation found for permdesc_canRequestTouchExplorationMode (940314268922270663) --> + <skip /> + <!-- no translation found for permlab_canRequestEnahncedWebAccessibility (1905232971331801453) --> + <skip /> + <!-- no translation found for permdesc_canRequestEnahncedWebAccessibility (4500520989321729676) --> + <skip /> <!-- no translation found for permlab_bindTextService (7358378401915287938) --> <skip /> <!-- no translation found for permdesc_bindTextService (8151968910973998670) --> @@ -1008,6 +1032,10 @@ <skip /> <!-- no translation found for permdesc_modifyNetworkAccounting (5443412866746198123) --> <skip /> + <!-- no translation found for permlab_accessNotifications (7673416487873432268) --> + <skip /> + <!-- no translation found for permdesc_accessNotifications (458457742683431387) --> + <skip /> <!-- no translation found for policylab_limitPassword (4497420728857585791) --> <skip /> <!-- no translation found for policydesc_limitPassword (3252114203919510394) --> @@ -1298,6 +1326,8 @@ <skip /> <!-- no translation found for lockscreen_access_pattern_detected (4988730895554057058) --> <skip /> + <!-- no translation found for keyguard_accessibility_widget_changed (5678624624681400191) --> + <skip /> <!-- no translation found for keyguard_accessibility_add_widget (8273277058724924654) --> <skip /> <!-- no translation found for keyguard_accessibility_widget_empty_slot (1281505703307930757) --> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 94f6263..e6d142b 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Stocare"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Accesează stocarea USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Accesează cardul SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funcții de accesibilitate"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funcții pe care tehnologia de asistare le poate solicita."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"dezactivare sau modificare bare de stare"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite aplicaţiei să dezactiveze bara de stare sau să adauge şi să elimine pictograme de sistem."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"bară de stare"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicaţiei să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locaţii pentru a vă avertiza cu privire la situaţiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcţionarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"trimitere mesaje SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicaţiei să trimită mesaje SMS, ceea ce ar putea determina apariţia unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"trimitere mesaje SMS fără confirmare"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Permite aplicaţiei să trimită mesaje SMS, ceea ce ar putea determina apariţia unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"trimitere de evenimente de tipul „răspuns prin mesaj”"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Permite aplicației să trimită solicitări altor aplicații de mesagerie pentru a gestiona evenimentele de tipul „răspuns prin mesaj” pentru apelurile primite."</string> <string name="permlab_readSms" msgid="8745086572213270480">"citeşte mesajele text (SMS sau MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite aplicaţiei să citească mesajele SMS stocate pe tabletă sau pe cardul SIM. În acest fel, aplicaţia poate citi toate mesajele SMS, indiferent de conţinutul sau de gradul de confidenţialitate al acestora."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permite aplicaţiei să citească mesajele SMS stocate pe telefon sau pe cardul SIM. În acest fel, aplicaţia poate citi toate mesajele SMS, indiferent de conţinutul sau de gradul de confidenţialitate al acestora."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Plasează Managerul de activităţi într-o stare de închidere. Nu efectuează o închidere completă."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"împiedicare comutare între aplicaţii"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Împiedică trecerea utilizatorului la o altă aplicaţie."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obținere informații despre aplicația curentă"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite proprietarului să preia informațiile private despre aplicația curentă în prim-planul ecranului."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorizare şi control asupra lansării tuturor aplicaţiilor"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite aplicaţiei să monitorizeze şi să controleze modul în care sistemul lansează activităţi. Aplicaţiile rău intenţionate pot să compromită sistemul în întregime. Această permisiune este necesară doar pentru dezvoltare şi niciodată pentru utilizarea normală."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"trimitere mesaj difuzat privind extragerea din pachet"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite unei aplicaţii să citească datele actuale privind utilizarea la nivel redus a bateriei. Cu această permisiune, aplicaţia poate afla informaţii detaliate despre aplicaţiile pe care le utilizaţi."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifică statistici referitoare la baterie"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite aplicaţiei să modifice statisticile colectate despre baterie. Nu se utilizează de aplicaţiile obişnuite."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"preluarea statisticilor privind operațiile aplicației"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Permite aplicației să preia statisticile colectate privind operațiile aplicației. Nu se utilizează de aplicațiile obișnuite."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificarea statisticilor privind utilizarea aplicației"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite aplicației să modifice statisticile colectate despre utilizarea aplicației. Nu se utilizează de aplicațiile obișnuite."</string> <string name="permlab_backup" msgid="470013022865453920">"controlare copiere de rezervă şi restabilire a sistemului"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Permite aplicaţiei să controleze mecanismul de copiere de rezervă şi de restabilire al sistemului. Nu se utilizează de aplicaţiile obişnuite."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmă o operaţie completă de copiere de rezervă sau de restabilire"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unei metode de introducere. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"conectare la un serviciu de accesibilitate"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unui serviciu de accesibilitate. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"solicitare de explorare prin atingere"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Permite proprietarului să solicite un mod de interacțiune în care elementele atinse sunt rostite cu voce tare, iar interfața de utilizare poate fi explorată prin gesturi."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"solicitare de accesibilitate mai bună la internet"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Permite proprietarului să solicite activarea îmbunătățirilor accesibilității web. De exemplu, instalarea unor scripturi pentru a mări accesibilitatea conținutului aplicațiilor."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"conectare la un serviciu text"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Permite proprietarului să se conecteze la o interfaţă de nivel superior a unui serviciu text (de ex., SpellCheckerService). Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"conectare la un serviciu VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite aplicaţiei să gestioneze politicile de reţea şi să definească regulile specifice aplicaţiilor."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificaţi modul de calcul al utilizării reţelei"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite aplicaţiei să modifice modul în care este calculată utilizarea reţelei pentru aplicaţii. Nu se utilizează de aplicaţiile obişnuite."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"accesare notificări"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Setaţi reguli pentru parolă"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Stabiliţi lungimea şi tipul de caractere permise în parolele pentru deblocarea ecranului."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizaţi încercările de deblocare a ecranului"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Modelul a fost şters"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Celulă adăugată"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Modelul a fost desenat"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d din %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Adăugaţi un widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Gol"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Zona de deblocare a fost extinsă."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 9ff53cd..fa623e0 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Память"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Доступ к USB-накопителю."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Доступ к SD-карте."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Специальные возможности"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Специальные возможности, которые можно запрашивать"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменять строку состояния"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"строка состояния"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Приложение получит доступ к сообщениям широковещательных SMS-служб, которые в некоторых странах используются для информирования населения об экстренных ситуациях. Вредоносные программы могут помешать работе устройства, на которое поступают такие сообщения."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"Отправка SMS-сообщений"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Приложение сможет отправлять SMS. Учтите, что вредоносные программы смогут отправлять сообщения без уведомления, что может привести к непредвиденным расходам."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"отправка SMS без подтверждения"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Приложение сможет отправлять SMS. Учтите, что вредоносные программы смогут отправлять сообщения без уведомления, что может привести к непредвиденным расходам."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"отвечать на звонки текстовыми сообщениями"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Приложение сможет отправлять запросы другим программам обмена сообщениями, чтобы на звонки можно было отвечать текстовыми сообщениями."</string> <string name="permlab_readSms" msgid="8745086572213270480">"Просмотр SMS и MMS"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Приложение сможет просматривать SMS-сообщения, сохраненные на устройстве или SIM-карте, независимо от содержания или настроек конфиденциальности."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Приложение сможет просматривать SMS-сообщения, сохраненные на устройстве или SIM-карте, независимо от содержания или настроек конфиденциальности."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Завершает работу диспетчера активности. Не выполняет полное завершение работы."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"запретить переключение приложений"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Запрещает пользователям переключаться между приложениями."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"показывать информацию о текущем приложении"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"На экране будут отображаться сведения о текущем приложении."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"отслеживание и управление запуском всех приложений"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Приложение сможет отслеживать запуск системных процессов и управлять им. Вредоносные программы смогут получить полный контроль над системой. Это разрешение необходимо только для разработки и не нужно в обычном режиме."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"отправлять рассылку об удалении пакета"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Разрешает приложению получать данные об использовании батареи на низшем уровне. В результате оно может иметь доступ к информации об используемых вами программах."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"изменять статистику батареи"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Приложение сможет изменять собранную статистику использования заряда батареи. Это разрешение не используется обычными приложениями."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"получать статистику операций в приложениях"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Приложение сможет получать собранную статистику операций в приложениях. Это разрешение не используется обычными программами."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"изменять статистику операций в приложениях"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Приложение сможет изменять собранную статистику операций в приложениях. Это разрешение не используется обычными приложениями."</string> <string name="permlab_backup" msgid="470013022865453920">"управление резервным копированием и восстановлением системы"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Приложение сможет управлять механизмами резервного копирования и восстановления системы. Это разрешение не используется обычными приложениями."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"подтверждать полное резервное копирование или восстановление"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Приложение сможет подключаться к базовому интерфейсу системы ввода. Это разрешение не используется обычными приложениями."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"подключаться к службе спецвозможностей"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Приложение сможет подключаться к базовому интерфейсу службы специальных возможностей. Это разрешение не используется обычными приложениями."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"запрашивать функцию \"Изучение касанием\""</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Владелец устройства сможет запрашивать включение режима \"Изучение касанием\", чтобы названия элементов управления озвучивались."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"запрашивать установку веб-скриптов"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Владелец устройства сможет запрашивать установку скриптов для повышения доступности веб-контента."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"привязка к службе текстовых сообщений"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Позволяет подключаться к базовому интерфейсу службы текстовых сообщений (например, SpellCheckerService). Это разрешение не используется обычными приложениями."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"подключаться к VPN-службе"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Приложение сможет управлять сетевыми политиками и определять правила для отдельных приложений."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"изменение учета использования сети"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Приложение сможет изменять порядок расчета использования сетевых ресурсов различными программами. Это разрешение не используется обычными приложениями."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"доступ к уведомлениям"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Приложение сможет получать, проверять и удалять уведомления, включая те, что опубликованы другими приложениями."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Правила выбора паролей"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролировать длину и символы при вводе паролей для снятия блокировки экрана."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Отслеживать попытки снятия блокировки экрана"</string> @@ -750,7 +764,7 @@ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неверный PIN-код."</string> <string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string> <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Экстренная служба"</string> - <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Сеть не найдена"</string> + <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нет сигнала"</string> <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран заблокирован."</string> <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Для разблокировки нажмите \"Меню\"."</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Графический ключ сброшен"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Ячейка добавлена"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Графический ключ введен"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Виджет %2$d из %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Добавить виджет"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Пусто"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Область разблокировки развернута"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 8ea6632..5f49e68 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Ukladací priestor"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Prístup do ukl. priestoru USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Prístup na kartu SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funkcie zjednodušenia ovládania"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkcie, ktoré môže vyžadovať nápomocná technológia."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"zakázanie alebo zmeny stavového riadka"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"stavový riadok"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Umožňuje aplikácii čítať správy Cell Broadcast prijaté vaším zariadením. Upozornenia Cell Broadcast sú doručované na určitých miestach a upozorňujú na núdzové situácie. Škodlivé aplikácie môžu pri prijatí núdzovej správy Cell Broadcast narušiť výkonnosť alebo prevádzku vášho zariadenia."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"odosielať správy SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Umožňuje aplikácii odosielať správy SMS. Môže to mať za následok účtovanie neočakávaných poplatkov. Škodlivé aplikácie vám môžu spôsobiť výdavky odosielaním správ bez vášho potvrdenia."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"posielať správy SMS bez potvrdenia"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Umožňuje aplikácii odosielať správy SMS. Môže to mať za následok účtovanie neočakávaných poplatkov. Škodlivé aplikácie vám môžu spôsobiť výdavky odosielaním správ bez vášho potvrdenia."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"odosielanie udalostí typu „odpovedzte správou“"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Umožňuje aplikácii odosielať žiadosti ostatným aplikáciám na posielanie správ s cieľom spracovania udalostí typu „odpovedzte správou“ pre prichádzajúce hovory."</string> <string name="permlab_readSms" msgid="8745086572213270480">"čítať textové správy (SMS alebo MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Umožňuje aplikácii čítať správy SMS uložené v tablete alebo na karte SIM. Toto povolenie umožňuje aplikácii čítať správy SMS bez ohľadu na ich obsah alebo dôvernosť."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Umožňuje aplikácii čítať správy SMS uložené v telefóne alebo na karte SIM. Toto povolenie umožňuje aplikácii čítať správy SMS bez ohľadu na ich obsah alebo dôvernosť."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Uvedie správcu činností do vypnutého stavu. Úplné vypnutie však nenastane."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zabrániť prepínaniu aplikácií"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Zabráni používateľovi prepnúť na inú aplikáciu."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"získať informácie o aktuálnej aplikácii"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Umožňuje držiteľovi povolenia načítať súkromné informácie o aktuálnej aplikácii v popredí obrazovky."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"sledovať a ovládať všetky spustenia aplikácií"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Umožňuje aplikácii sledovať a ovládať spúšťanie aktivít systémom. Škodlivé aplikácie môžu systém úplne ovládnuť. Toto povolenie je potrebné len na účely vývoja, nikdy nie na bežné používanie."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"odoslanie vysielania o odstránení balíčka"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Umožňuje aplikácii čítať aktuálne údaje nízkej úrovne o používaní batérie. Pomocou tejto funkcie môže aplikácia zistiť podrobnosti o tom, ktoré aplikácie používate."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"zmena štatistických údajov o batérii"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Umožňuje aplikácii zmeniť zhromaždené štatistické údaje o batérii. Bežné aplikácie toto nastavenie nepoužívajú."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"získať štatistické údaje o fungovaní aplikácií"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Umožňuje aplikácii získať zhromaždené štatistické údaje o fungovaní aplikácií. Bežné aplikácie toto nastavenie nepoužívajú."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"upraviť štatistické údaje o fungovaní aplikácií"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Umožňuje aplikácii zmeniť zhromaždené štatistické údaje o fungovaní aplikácií. Bežné aplikácie toto nastavenie nepoužívajú."</string> <string name="permlab_backup" msgid="470013022865453920">"Ovládať zálohovanie a obnovu systému"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Umožňuje aplikácii ovládať mechanizmus na zálohovanie a obnovu údajov systému. Bežné aplikácie toto nastavenie nepoužívajú."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potvrdenie operácie úplnej zálohy alebo úplného obnovenia"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania metódy vstupu. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"viazať na službu zjednodušeného ovládania"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania služby zjednodušeného ovládania. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"vyžiadať preskúmanie dotykom"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Držiteľovi umožňuje požiadať o interaktívny režim, v ktorom aplikácia reaguje na dotyky položiek vyslovením ich názvu a v ktorom môže používateľ preskúmať používateľské rozhranie pomocou gest."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"vyžiadať zlepšenie dostupnosti webu"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Držiteľovi umožňuje, aby vyžiadal povolenie vylepšení prístupu k webu, napríklad inštaláciu skriptov, ktoré uľahčujú prístup k obsahu aplikácie."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"väzba na textovú službu"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania textovej služby (napr. SpellCheckerService). Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"Zaviazať k službe VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Umožňuje aplikácii spravovať pravidlá siete a definovať pravidlá pre konkrétnu aplikáciu."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"zmeniť kontrolu používania siete"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Umožňuje aplikácii upraviť používanie siete jednotlivými aplikáciami. Bežné aplikácie toto nastavenie nepoužívajú."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"prístup k upozorneniam"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikácii načítať, zobrazovať a mazať upozornenia vrátane tých, ktoré boli uverejnené inými aplikáciami."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Nastaviť pravidlá pre heslo"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Ovládanie dĺžky hesiel na odomknutie obrazovky a v nich používané znaky."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Sledovať pokusy o odomknutie obrazovky"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Bezpečnostný vzor bol vymazaný"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Bunka bola pridaná"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Bezpečnostný vzor bol dokončený"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Miniaplikácia %2$d z %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Pridať miniaplikáciu."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prázdne"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Oblasť na odomknutie bola rozšírená."</string> @@ -1092,11 +1107,11 @@ <string name="volume_icon_description_incall" msgid="8890073218154543397">"Hlasitosť hovoru"</string> <string name="volume_icon_description_media" msgid="4217311719665194215">"Hlasitosť médií"</string> <string name="volume_icon_description_notification" msgid="7044986546477282274">"Hlasitosť upozornení"</string> - <string name="ringtone_default" msgid="3789758980357696936">"Predvolený vyzváňací tón"</string> - <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Predvolený vyzváňací tón (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> + <string name="ringtone_default" msgid="3789758980357696936">"Predvolený tón zvonenia"</string> + <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Predvolený tón zvonenia (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> <string name="ringtone_silent" msgid="7937634392408977062">"Žiadny"</string> <string name="ringtone_picker_title" msgid="3515143939175119094">"Vyzváňacie tóny"</string> - <string name="ringtone_unknown" msgid="5477919988701784788">"Neznámy vyzváňací tón"</string> + <string name="ringtone_unknown" msgid="5477919988701784788">"Neznámy tón zvonenia"</string> <plurals name="wifi_available"> <item quantity="one" msgid="6654123987418168693">"K dispozícii je sieť Wi-Fi"</item> <item quantity="other" msgid="4192424489168397386">"K dispozícii sú siete Wi-Fi."</item> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index e3802a5..aa240de 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Prostor za shranjevanje"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Dostop do pomnilnika USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Dostop do kartice SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funkcije za ljudi s posebnimi potrebami"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkcije, ki jih lahko zahteva tehnologija za ljudi s posebnimi potrebami."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"onemogočanje ali spreminjanje vrstice stanja"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Programom omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"vrstica stanja"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Omogoča aplikaciji branje sporočil oddaje v celici, ki jih prejme naprava. Opozorila oddaje v celici so dostavljena na nekaterih lokacijah, da vas opozorijo na izredne razmere. Zlonamerne aplikacije lahko vplivajo na delovanje naprave, ko dobi sporočilo oddaje v celici."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"pošiljanje sporočil SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Aplikaciji omogoča pošiljanje SMS-ov. Zaradi tega lahko pride do nepričakovanih stroškov. Zlonamerne aplikacije lahko pošiljajo sporočila brez vaše potrditve, kar vas lahko drago stane."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"pošiljanje SMS-ov brez potrditve"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Aplikaciji omogoča pošiljanje SMS-ov. Zaradi tega lahko pride do nepričakovanih stroškov. Zlonamerne aplikacije lahko pošiljajo sporočila brez vaše potrditve, kar vas lahko drago stane."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"pošiljanje dogodkov z odgovori prek sporočil"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Aplikaciji dovoli, da pošilja zahteve drugim aplikacijam za sporočila za obravnavanje dogodkov z odgovori prek sporočil za dohodne klice."</string> <string name="permlab_readSms" msgid="8745086572213270480">"branje sporočil (SMS ali MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Aplikaciji omogoča branje SMS-ov, shranjenih v tabličnem računalniku ali na kartici SIM. S tem lahko aplikacija bere vse SMS-e, ne glede na njihovo vsebino ali zaupnost."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Aplikaciji omogoča branje SMS-ov, shranjenih v telefonu ali na kartici SIM. S tem lahko aplikacija bere vse SMS-e, ne glede na njihovo vsebino ali zaupnost."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Upravitelja dejavnosti preklopi v stanje za zaustavitev. Ne izvede celotne zaustavitve."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"preprečevanje preklopa programov"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Uporabniku preprečuje preklop v drug program."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pridobivanje podatkov o trenutni aplikaciji"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Omogoča imetniku pridobivanje zasebnih podatkov o trenutni aplikaciji v ospredju zaslona."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"spremljanje in nadzor vseh zagonov programov"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Programu omogoča spremljanje in nadziranje načina, kako sistem zažene dejavnosti. Zlonamerni programi lahko v celoti ogrozijo varnost sistema. To dovoljenje je potrebno samo za razvoj, vendar nikoli za običajno uporabo."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"pošiljanje oddaje brez paketa"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Aplikaciji omogoča branje podatkov o trenutni nizki napolnjenosti akumulatorja. Aplikaciji lahko tudi dovoli dostop do podrobnosti o tem, katere aplikacije uporabljate."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"spreminjanje statističnih podatkov o akumulatorju"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Aplikaciji omogoča spreminjanje zbranih statističnih podatkov o akumulatorju. Ni primerno za uporabo z običajnimi aplikacijami."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"pridobi statistične podatke o delovanju aplikacij"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Aplikaciji dovoli pridobivanje zbranih statističnih podatkov o delovanju aplikacij. Ni za uporabo v navadnih aplikacijah."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"spreminjanje statističnih podatkov o delovanju aplikacije"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Aplikaciji dovoli spreminjanje zbranih statističnih podatkov o delovanju aplikacij. Ni za uporabo v navadnih aplikacijah."</string> <string name="permlab_backup" msgid="470013022865453920">"nadzor varnostnega kopiranja sistema in obnovitev"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Programu omogoča nadzor mehanizma za varnostno kopiranje in obnovitev sistema. Ni za uporabo z navadnimi programi."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potrditev popolnega varnostnega kopiranja ali obnovitve"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lastniku omogoča, da se poveže z vmesnikom načina vnosa najvišje ravni. Tega nikoli ni treba uporabiti za navadne programe."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"povezovanje s storitvijo za ljudi s posebnimi potrebami"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lastniku omogoča povezovanje z vmesnikom najvišje ravni storitve za ljudi s posebnimi potrebami. Tega nikoli ni treba uporabiti za navadne aplikacije."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"zahteva za raziskovanje z dotikom"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Imetniku dovoli, da zahteva interaktivni način delovanja, v katerem se dotaknjene možnosti izgovorijo na glas in je mogoče uporabniški vmesnik raziskati s potezami."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"zahteva za izboljšano spletno dostopnost"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Imetniku dovoli, da zahteva omogočanje izboljšav spletne dostopnosti, na primer namestitev skriptov, ki naredijo vsebino aplikacije dostopnejšo."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"poveži z besedilno storitvijo"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Dovoljuje, da se lastnik poveže z vmesnikom besedilne storitve najvišje ravni (npr. SpellCheckerService). Tega nikoli ni treba uporabiti za navadne programe."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"povezava s storitvijo navideznega zasebnega omrežja"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Programu omogoča upravljanje pravilnikov o omrežju in določanje pravil za program."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"spremeni obračunavanje uporabe omrežja"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Programu omogoča, da spremeni uporabo omrežja na podlagi programov. Ni za uporabo z navadnimi programi."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"dostop do obvestil"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Dovoli aplikaciji, da prenese, razišče in izbriše obvestila, tudi tista, ki so jih objavile druge aplikacije."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavitev pravil za geslo"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih za odklepanje zaslona."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"nadzor nad poskusi odklepanja zaslona"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Vzorec je izbrisan"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Celica je dodana"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Vzorec je končan"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Pripomoček %2$d za %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Dodajanje pripomočka."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Prazno"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Območje odklepanja razširjeno."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 36994fb..7a3ff07 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Складиште"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Приступите USB меморији."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Приступ SD картици."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Функције приступачности"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Функције које технологија за помоћ може да захтева."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"онемогућавање или измена статусне траке"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"статусна трака"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"слање SMS порука"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Дозвољава апликацији да шаље SMS поруке. Ово може да доведе до неочекиваних трошкова. Злонамерне апликације могу да шаљу поруке без ваше потврде, што може да изазове трошкове."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"слање SMS порука без потврде"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Дозвољава апликацији да шаље SMS поруке. Ово може да доведе до неочекиваних трошкова. Злонамерне апликације могу да шаљу поруке без ваше потврде, што може да изазове трошкове."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"слање догађаја одговора преко поруке"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Омогућава апликацији да другим апликацијама за размену порука шаље захтеве за обраду догађаја одговора преко порука за долазне позиве."</string> <string name="permlab_readSms" msgid="8745086572213270480">"читање текстуалних порука (SMS или MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Дозвољава апликацији да чита SMS поруке ускладиштене на таблету или SIM картици. Ово омогућава апликацији да чита све SMS поруке, без обзира на садржај или поверљивост."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Дозвољава апликацији да чита SMS поруке ускладиштене на телефону или SIM картици. Ово омогућава апликацији да чита све SMS поруке, без обзира на садржај или поверљивост."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Ставља менаџера активности у стање искључивања. Не искључује га у потпуности."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"спречавање пребацивања са једне апликације на другу"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Спречава да корисник пређе на другу апликацију."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"преузимање информација о актуелној апликацији"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Дозвољава власнику да преузима приватне информације о актуелној апликацији у првом плану екрана."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"надгледање и контрола покретања свих апликација"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Дозвољава апликацији да прати начин на који систем покреће активности и да њиме управља. Злонамерне апликације могу у потпуности да угрозе систем. Ова дозвола је потребна само за програмирање, а никада за уобичајено коришћење."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"слање емитовања уклоњеног пакета"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Дозвољава апликацији да чита опште податке о тренутној употреби батерије на измаку. Можда ће апликацији дозволити да сазна детаљне информације о томе које апликације користите."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"измена статистике о батерији"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Дозвољава апликацији да мења прикупљену статистику о батерији. Не користе је обичне апликације."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"преузимање статистике о функционисању апликације"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Дозвољава апликацији да преузима прикупљену статистику о функционисању апликације. Не користе је уобичајене апликације."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"измена статистике о функционисању апликације"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Дозвољава апликацији да измени прикупљену статистику о функционисању апликације. Не користе је уобичајене апликације."</string> <string name="permlab_backup" msgid="470013022865453920">"контрола резервне копије система и враћање почетних вредности"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Дозвољава апликацији да управља системским механизмом за прављење резервне копије и враћање. Не користе је уобичајене апликације."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"потврда прављења пуне резервне копије или операције враћања"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Омогућава да се власник обавеже на интерфејс методе уноса највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"повезивање са услугом приступачности"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Дозвољава власнику да се повеже са интерфејсом услуге приступачности највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"захтевање истраживања додиром"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Омогућава носиоцу да захтева режим интеракције у коме се називи додирнутих ставки изговарају наглас, а кориснички интерфејс може да се истражује покретима."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"захтевање побољшане приступачности вебу"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Омогућава носиоцу да захтева омогућавање побољшања приступачности веба. На пример, инсталирање скрипти да би садржај апликације постао приступачнији."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"обавезивање на текстуалну услугу"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Омогућава власнику да се обавеже на интерфејс текстуалне услуге највишег нивоа (нпр. SpellCheckerService). Обичне апликације никада не би требало да је користе."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"везивање за VPN услугу"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Дозвољава апликацији да управља смерницама за мрежу и одређује посебна правила за апликацију."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"измените обрачунавање коришћења мреже"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дозвољава апликацији да измени начин на који апликације користе мрежу. Не користе је уобичајене апликације."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"приступ обавештењима"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозвољава апликацији да преузима, испитује и брише обавештења, укључујући она која постављају друге апликације."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Подешавање правила за лозинку"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролишите дужину и знакове дозвољене у лозинкама за откључавање екрана."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Надгледање покушаја откључавања екрана"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Образац је обрисан"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Ћелија је додата"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Образац је довршен"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Виџет %2$d од %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Додај виџет."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Празно"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Област откључавања је проширена."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 8058a8d..eca52e6 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Få åtkomst till USB-enheten."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Få åtkomst till SD-kortet."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Tillgänglighetsfunktioner"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funktioner som kan behövas med hjälpmedel."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"inaktivera eller ändra statusfält"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"statusfält"</string> @@ -243,13 +245,13 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Appen tillåts läsa SMS som skickas till din enhet. På vissa platser skickas SMS för att varna för nödsituationer. Skadliga appar kan påverka enhetens prestanda eller funktionalitet när du får ett meddelande om en nödsituation via SMS."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"skicka SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"skicka SMS utan bekräftelse"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"skicka svar via meddelanden"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Tillåter att appen skickar förfrågningar till andra meddelandeappar för att kunna skicka meddelanden som svar på inkommande samtal."</string> <string name="permlab_readSms" msgid="8745086572213270480">"läsa dina textmeddelanden (SMS eller MMS)"</string> - <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillåter att appen läser SMS som sparats på pekdatorn eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string> + <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillåter att appen läser SMS som sparats på surfplattan eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Tillåter att appen läser SMS som sparats på mobilen eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string> <string name="permlab_writeSms" msgid="3216950472636214774">"redigera dina textmeddelanden (SMS eller MMS)"</string> - <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"Tillåter att appen skriver till SMS som lagras på pekdatorn eller SIM-kortet. Skadliga appar kan radera dina meddelanden."</string> + <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"Tillåter att appen skriver till SMS som lagras på surfplattan eller SIM-kortet. Skadliga appar kan radera dina meddelanden."</string> <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"Tillåter att appen skriver till SMS som lagras på mobilen eller SIM-kortet. Skadliga appar kan radera dina meddelanden."</string> <string name="permlab_receiveWapPush" msgid="5991398711936590410">"ta emot textmeddelanden (WAP)"</string> <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Sätter aktivitetshanteraren i avstängningsläge. Utför inte en fullständig avstängning."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"förhindrar programbyten"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hindrar användaren från att byta till en annan app."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"hämta information om aktuell app"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Innehavaren tillåts att hämta privat information om den app som för tillfället är i förgrunden på skärmen."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"övervaka och styra alla appar som öppnas"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Tillåter att appen övervakar och styr hur systemet startar aktiviteter. Skadliga appar kan kompromettera systemet helt. Den här behörigheten behövs bara för programmering, aldrig för vanlig användning."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"skicka meddelande om borttaget paket"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillåter att en app läser de aktuella uppgifterna om låg batterianvändningsnivå. Appen kan tillåtas få reda på detaljerade uppgifter om vilka appar du använder."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"ändra batteristatistik"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillåter att appen ändrar samlad batteristatistik. Används inte av vanliga appar."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hämta åtgärdsstatistik för appar"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Tillåter att appen hämtar samlad åtgärdsstatistik för appar. Används inte av vanliga appar."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"ändra appars åtgärdsstatistik"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillåter att appen ändrar samlad åtgärdsstatistik för appar. Används inte av vanliga appar."</string> <string name="permlab_backup" msgid="470013022865453920">"kontrollera säkerhetskopiering och återställning av systemet"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Tillåter att appen styr över systemets mekanism för säkerhetskopiering och återställning. Används inte av vanliga appar."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Bekräfta fullständig säkerhetskopia eller återställning"</string> @@ -330,7 +338,7 @@ <string name="permlab_freezeScreen" msgid="4708181184441880175">"frysa skärmen"</string> <string name="permdesc_freezeScreen" msgid="8558923789222670064">"Tillåter att appen tillfälligt fryser skärmen för övergång till helskärm."</string> <string name="permlab_injectEvents" msgid="1378746584023586600">"trycka på knappar och styrknappar"</string> - <string name="permdesc_injectEvents" product="tablet" msgid="206352565599968632">"Tillåter att appen levererar egna inmatningshändelser (knapptryckningar osv.) till andra appar. Skadliga appar kan använda detta för att kapa pekdatorn."</string> + <string name="permdesc_injectEvents" product="tablet" msgid="206352565599968632">"Tillåter att appen levererar egna inmatningshändelser (knapptryckningar osv.) till andra appar. Skadliga appar kan använda detta för att kapa surfplattan."</string> <string name="permdesc_injectEvents" product="default" msgid="653128057572326253">"Tillåter att appen levererar egna inmatningshändelser (knapptryckningar osv.) till andra appar. Skadliga appar kan använda detta för att kapa mobilen."</string> <string name="permlab_readInputState" msgid="469428900041249234">"registrera vad du skriver och vilka åtgärder du vidtar"</string> <string name="permdesc_readInputState" msgid="8387754901688728043">"Tillåter att appen övervakar knapparna som du trycker på, till och med när du använder andra appar (till exempel när du anger ett lösenord). Ska inte behövas för vanliga appar."</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en inmatningsmetod. Ska inte behövas för vanliga appar."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"bind till en tillgänglighetstjänst"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en tillgänglighetstjänst. Ska inte behövas för vanliga appar."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"begära beröringsstyrda gränssnittsfunktioner"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Innehavaren tillåts begära ett interaktionsläge där objekt som användaren trycker på läses upp och där gränssnittet kan användas med gester."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"begära tillgänglighetsfunktioner"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Innehavaren tillåts begära aktivering av tillgänglighetsfunktioner. Det kan t.ex. vara att installera skript från Google som gör appens innehåll mer tillgängligt."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"bind till en texttjänst"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Tillåter innehavaren att binda mot den högsta gränssnittsnivån i en texttjänst (t.ex. SpellCheckerService). Bör aldrig behövas för normala appar."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"bind till en VPN-tjänst"</string> @@ -357,7 +369,7 @@ <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"skicka Linux-signaler till appar"</string> <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"Tillåter att appen begär att den angivna signalen skickas till alla beständiga processer."</string> <string name="permlab_persistentActivity" msgid="8841113627955563938">"se till att appen alltid körs"</string> - <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör pekdatorn långsam."</string> + <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör surfplattan långsam."</string> <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör mobilen långsam."</string> <string name="permlab_deletePackages" msgid="184385129537705938">"ta bort appar"</string> <string name="permdesc_deletePackages" msgid="7411480275167205081">"Tillåter att appen tar bort Android-paket. Skadliga appar kan använda detta för att ta bort viktiga appar."</string> @@ -375,15 +387,15 @@ <string name="permlab_movePackage" msgid="3289890271645921411">"flytta appresurser"</string> <string name="permdesc_movePackage" msgid="319562217778244524">"Tillåter att appen flyttar appresurser från interna till externa medier och tvärtom."</string> <string name="permlab_readLogs" msgid="6615778543198967614">"läsa känsliga loggdata"</string> - <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"Tillåter att appen läser från systemets olika loggfiler. Det innebär att appen kan upptäcka allmän information om vad du gör med pekdatorn, vilket kan inkludera personlig eller privat information."</string> + <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"Tillåter att appen läser från systemets olika loggfiler. Det innebär att appen kan upptäcka allmän information om vad du gör med surfplattan, vilket kan inkludera personlig eller privat information."</string> <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"Tillåter att appen läser från systemets olika loggfiler. Det innebär att appen kan upptäcka allmän information om vad du gör med mobilen, vilket kan inkludera personlig eller privat information."</string> <string name="permlab_anyCodecForPlayback" msgid="715805555823881818">"använda alla medieavkodare för uppspelning"</string> <string name="permdesc_anyCodecForPlayback" msgid="8283912488433189010">"Tillåter att appen använder installerade medieavkodare för att avkoda media för uppspelning."</string> <string name="permlab_diagnostic" msgid="8076743953908000342">"läsa/skriva till resurser som ägs av diag"</string> <string name="permdesc_diagnostic" msgid="6608295692002452283">"Tillåter att appen läser och skriver till en resurs som ägs av diag-gruppen, till exempel filer i /dev. Detta kan eventuellt påverka systemets stabilitet och säkerhet. Detta bör ENDAST användas av tillverkaren eller operatören för maskinvaruspecifik diagnostik."</string> <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivera eller inaktivera appkomponenter"</string> - <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Tillåter att appen ändrar inställningen för om en komponent i en annan app ska aktiveras eller inte. Skadliga appar kan använda detta för att inaktivera viktiga funktioner i pekdatorn. Var försiktig med behörigheten, eftersom appkomponenter kan bli oanvändbara, inkonsekventa eller instabila."</string> - <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Tillåter att appen ändrar inställningen för om en komponent i en annan app ska aktiveras eller inte. Skadliga appar kan använda detta för att inaktivera viktiga funktioner i pekdatorn. Var försiktig med behörigheten, eftersom programkomponenter kan bli oanvändbara, inkonsekventa eller instabila."</string> + <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Tillåter att appen ändrar inställningen för om en komponent i en annan app ska aktiveras eller inte. Skadliga appar kan använda detta för att inaktivera viktiga funktioner i surfplattan. Var försiktig med behörigheten, eftersom appkomponenter kan bli oanvändbara, inkonsekventa eller instabila."</string> + <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Tillåter att appen ändrar inställningen för om en komponent i en annan app ska aktiveras eller inte. Skadliga appar kan använda detta för att inaktivera viktiga funktioner i surfplattan. Var försiktig med behörigheten, eftersom programkomponenter kan bli oanvändbara, inkonsekventa eller instabila."</string> <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"bevilja eller återkalla behörighet"</string> <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Tillåter att en app beviljar eller återkallar specifik behörighet, för sig själv eller andra appar. Skadlig programvara kan utnyttja detta för att få åtkomst till funktioner som du inte har beviljat behörighet till."</string> <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ange önskade appar"</string> @@ -395,16 +407,16 @@ <string name="permlab_writeGservices" msgid="2149426664226152185">"ändra kartan för Googles tjänster"</string> <string name="permdesc_writeGservices" msgid="1287309437638380229">"Tillåter att appen ändrar kartan för Googles tjänster. Används inte av vanliga appar."</string> <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"köra vid start"</string> - <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta pekdatorn och att pekdatorn blir långsammare i och med att appen hela tiden körs i bakgrunden."</string> + <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta surfplattan och att surfplattan blir långsammare i och med att appen hela tiden körs i bakgrunden."</string> <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta mobilen och att mobilen blir långsammare i och med att appen hela tiden körs i bakgrunden."</string> <string name="permlab_broadcastSticky" msgid="7919126372606881614">"Skicka sticky broadcast"</string> <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Tillåter att appen skickar sticky broadcasts, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string> <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillåter att appen skickar sticky broadcast, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string> <string name="permlab_readContacts" msgid="8348481131899886131">"läsa dina kontakter"</string> - <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillåter att appen läser kontaktuppgifter som sparats på pekdatorn, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> + <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillåter att appen läser kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillåter att appen läser kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> <string name="permlab_writeContacts" msgid="5107492086416793544">"ändra kontakterna"</string> - <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillåter att appen ändrar kontaktuppgifter som sparats på pekdatorn, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string> + <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillåter att appen ändrar kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillåter att appen ändrar kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"läs samtalslogg"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillåter att appen läser pekdatorns samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> @@ -421,10 +433,10 @@ <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skriv till mitt sociala flöde"</string> <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Tillåter att appen visar sociala uppdateringar från dina vänner. Var försiktig när du delar information – med den här behörigheten tillåts appen att generera meddelanden som kan se ut att komma från en vän. Observera att den här behörigheten kanske inte är tillämplig på alla sociala nätverk."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"läsa kalenderuppgifter plus konfidentiell information"</string> - <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på pekdatorn, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string> + <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string> <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Tillåter att appen läser alla kalenderuppgifter som sparats på mobilen, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string> <string name="permlab_writeCalendar" msgid="8438874755193825647">"lägga till eller ändra kalenderuppgifter och skicka e-post till gäster utan ägarens vetskap"</string> - <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på pekdatorn, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string> + <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på surfplattan, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string> <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string> <string name="permlab_accessMockLocation" msgid="8688334974036823330">"skenplatser för att testa"</string> <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Skapa skenplatser för tester eller installera en ny platsleverantör. Detta innebär att appen tillåts åsidosätta den plats och/eller status som returneras av andra platskällor, till exempel GPS eller platsleverantörer."</string> @@ -452,11 +464,11 @@ <string name="permdesc_camera" msgid="8497216524735535009">"Tillåter att appen tar bilder och spelar in videor med kameran. Med den här behörigheten tillåts appen att använda kameran när som helst utan ditt godkännande."</string> <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"inaktivera surfplattan permanent"</string> <string name="permlab_brick" product="default" msgid="8337817093326370537">"inaktivera telefonen permanent"</string> - <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"Tillåter att appen inaktiverar hela pekdatorn permanent. Detta är mycket farligt."</string> + <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"Tillåter att appen inaktiverar hela surfplattan permanent. Detta är mycket farligt."</string> <string name="permdesc_brick" product="default" msgid="5788903297627283099">"Tillåter att appen inaktiverar hela mobilen permanent. Detta är mycket farligt."</string> <string name="permlab_reboot" product="tablet" msgid="3436634972561795002">"tvinga omstart av surfplatta"</string> <string name="permlab_reboot" product="default" msgid="2898560872462638242">"tvinga omstart av telefon"</string> - <string name="permdesc_reboot" product="tablet" msgid="8172056180063700741">"Tillåter att appen tvingar pekdatorn att starta om."</string> + <string name="permdesc_reboot" product="tablet" msgid="8172056180063700741">"Tillåter att appen tvingar surfplattan att starta om."</string> <string name="permdesc_reboot" product="default" msgid="5326008124289989969">"Tillåter att appen tvingar mobilen att starta om."</string> <string name="permlab_mount_unmount_filesystems" product="nosdcard" msgid="2927361537942591841">"få åtkomst till USB-lagringen"</string> <string name="permlab_mount_unmount_filesystems" product="default" msgid="4402305049890953810">"få åtkomst till SD-kortets filsystem"</string> @@ -503,11 +515,11 @@ <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string> <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"förhindra att surfplattan går in i viloläge"</string> <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"förhindra att telefonen sätts i viloläge"</string> - <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillåter att appen förhindrar att pekdatorn går in i viloläge."</string> + <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillåter att appen förhindrar att surfplattan går in i viloläge."</string> <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Tillåter att appen förhindrar att mobilen går in i viloläge."</string> <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"slå på eller stänga av surfplattan"</string> <string name="permlab_devicePower" product="default" msgid="4928622470980943206">"sätta på eller stänga av telefonen"</string> - <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"Tillåter att appen slår på eller stänger av pekdatorn."</string> + <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"Tillåter att appen slår på eller stänger av surfplattan."</string> <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"Tillåter att appen slår på eller stänger av mobilen."</string> <string name="permlab_factoryTest" msgid="3715225492696416187">"kör i fabrikstestläge"</string> <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"Köra som ett testläge för tillverkaren på låg nivå. På så sätt får du fullständig åtkomst till surfplattans maskinvara. Är endast tillgänglig när surfplattan körs i tillverkarens testläge."</string> @@ -527,7 +539,7 @@ <string name="permlab_accountManagerService" msgid="4829262349691386986">"fungera som AccountManagerService"</string> <string name="permdesc_accountManagerService" msgid="1948455552333615954">"Tillåter att appen anropar AccountAuthenticators."</string> <string name="permlab_getAccounts" msgid="1086795467760122114">"hitta konton på enheten"</string> - <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Tillåter att appen hämtar en lista över alla kända konton på pekdatorn. Detta kan inkludera konton som har skapats av appar som du har installerat."</string> + <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Tillåter att appen hämtar en lista över alla kända konton på surfplattan. Detta kan inkludera konton som har skapats av appar som du har installerat."</string> <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat."</string> <string name="permlab_authenticateAccounts" msgid="5265908481172736933">"skapa konton och ange lösenord"</string> <string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"Tillåter att appen använder AccountManagers kontoautentiseringsfunktioner, bland annat funktioner för att skapa konton samt hämta och ange lösenord för dem."</string> @@ -552,15 +564,15 @@ <string name="permlab_changeWifiState" msgid="6550641188749128035">"anslut och koppla från Wi-Fi"</string> <string name="permdesc_changeWifiState" msgid="7137950297386127533">"Tillåter att appen ansluter till och kopplar från Wi-Fi-åtkomstpunkter samt gör ändringar i enhetens konfiguration för Wi-Fi-nätverk."</string> <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillåt Wi-Fi multicast-mottagning"</string> - <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här pekdatorn. Detta drar mer batteri än när multicastläget inte används."</string> + <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här surfplattan. Detta drar mer batteri än när multicastläget inte används."</string> <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här mobilen. Detta drar mer batteri än när multicastläget inte används."</string> <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"få åtkomst till Bluetooth-inställningar"</string> - <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tillåter att appen konfigurerar den lokala Bluetooth-pekdatorn samt upptäcker och parkopplar den med fjärranslutna enheter."</string> + <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tillåter att appen konfigurerar den lokala Bluetooth-surfplattan samt upptäcker och parkopplar den med fjärranslutna enheter."</string> <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter."</string> <string name="permlab_accessWimaxState" msgid="4195907010610205703">"ansluta till och koppla från WiMAX"</string> <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk."</string> <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string> - <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter pekdatorn till eller kopplar från WiMAX-nätverk."</string> + <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string> <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string> <string name="permlab_bluetooth" msgid="6127769336339276828">"koppla till Bluetooth-enheter"</string> <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string> @@ -587,7 +599,7 @@ <string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testa åtkomst till skyddad lagringsenhet"</string> <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Tillåter att appen testar behörighet till USB-enheter för användning på framtida enheter."</string> <string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Tillåter appen att testa behörighet till SD-kortet för användning på framtida enheter."</string> - <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ändra eller ta bort innehållet"</string> + <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ändra eller ta bort innehåll på USB-enheten"</string> <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ändra eller ta bort innehåll på SD-kortet"</string> <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Gör att app skriver till USB."</string> <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string> @@ -605,17 +617,19 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Tillåter att appen hanterar nätverkspolicyer och definierar appspecifika regler."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ändra nätverksredovisningen"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillåter att appen ändrar hur nätverksanvändning redovisas för appar. Används inte av vanliga appar."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"få åtkomst till meddelanden"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillåter att appen hämtar, granskar och raderar meddelanden, även sådana som skickats av andra appar."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Ange lösenordsregler"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Bestäm hur många och vilka tecken som är tillåtna i skärmlåsets lösenord."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string> - <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås pekdatorn eller ta bort alla data från pekdatorn om för många felaktiga försök görs."</string> + <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string> <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string> <string name="policylab_resetPassword" msgid="2620077191242688955">"Ändra skärmlåsets lösenord"</string> <string name="policydesc_resetPassword" msgid="605963962301904458">"Ändra skärmlåsets lösenord."</string> <string name="policylab_forceLock" msgid="2274085384704248431">"Lås skärmen"</string> <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollera hur och när skärmlåset aktiveras."</string> <string name="policylab_wipeData" msgid="3910545446758639713">"Radera alla data"</string> - <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ta bort data från pekdatorn utan förvarning genom att återställa standardinställningarna."</string> + <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string> <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ange vilken global proxyserver som ska användas när policyn är aktiverad. Endast den första enhetsadministratören anger den faktiska globala proxyservern."</string> @@ -786,9 +800,9 @@ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp pekdatorn med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer pekdatorn att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string> <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string> <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Grafiskt lösenord har tagits bort"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"En cell har lagts till"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Grafiskt lösenord har slutförts"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d av %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Lägg till en widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tom"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Expanderad upplåsningsyta."</string> @@ -869,7 +884,7 @@ <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"läsa dina bokmärken och din historik på webben"</string> <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Tillåter att appen läser historiken för besökta sidor och alla bokmärken i webbläsaren. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"skriva bokmärken och historik på webben"</string> - <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på pekdatorn. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> + <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på surfplattan. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> <string name="permlab_setAlarm" msgid="1379294556362091814">"ställa in ett alarm"</string> <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string> @@ -905,7 +920,7 @@ <string name="searchview_description_submit" msgid="2688450133297983542">"Skicka fråga"</string> <string name="searchview_description_voice" msgid="2453203695674994440">"Röstsökning"</string> <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Aktivera Explore by Touch?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vill aktivera Explore by Touch. När funktionen är aktiv kan du höra eller se beskrivningar av vad du har under fingret eller utföra gester för att göra saker med pekdatorn."</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vill aktivera Explore by Touch. När funktionen är aktiv kan du höra eller se beskrivningar av vad du har under fingret eller utföra gester för att göra saker med surfplattan."</string> <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vill aktivera Explore by Touch. När funktionen är aktiv kan du höra eller se beskrivningar av vad du har under fingret eller utföra gester för att göra saker med telefonen."</string> <string name="oneMonthDurationPast" msgid="7396384508953779925">"för 1 månad sedan"</string> <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"För mer än en månad sedan"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 2789146..554921b 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -169,7 +169,7 @@ <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Huduma ambazo zinakugharimu pesa"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Fanya mambo ambayo yanaweza kukugharimu pesa."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Ujumbe wako"</string> - <string name="permgroupdesc_messages" msgid="7821999071003699236">"Soma na kuandika SMS, barua pepe, na jumbe zako zingine."</string> + <string name="permgroupdesc_messages" msgid="7821999071003699236">"Soma na uandike SMS, barua pepe, na mawasiliano mengine."</string> <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"Maelezo yako ya kibinafsi"</string> <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"Kufikia moja kwa moja taarifa kukuhusu, iliyoakibishwa kwenye kadi yako ya anwani."</string> <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"Taarifa yako ya kijamii"</string> @@ -190,8 +190,8 @@ <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Soma maneno katika kamusi ya mtumiaji."</string> <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Andika Kamusi ya Mtumiaji"</string> <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Ongeza maneno katika kamusi mtumiaji."</string> - <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Vialamisho na Historia"</string> - <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Kufikia moja kwa moja vialamisho na historia ya kivinjari"</string> + <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Alamisho na Historia"</string> + <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Kufikia, moja kwa moja, alamisho na historia ya kivinjari."</string> <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Kengele"</string> <string name="permgroupdesc_deviceAlarms" msgid="4769356362251641175">"Weka saa ya kengele."</string> <string name="permgrouplab_voicemail" msgid="4162237145027592133">"Barua ya sauti"</string> @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Hifadhi"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Fikia hifadhi ya USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Fikia kadi ya SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Vipengele vya ufikiaji"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Vipengee ambavyo teknolojia saidizi inaweza kuomba."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"zima au rekebisha mwambaa hali"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa ikoni za mfumo."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"mwamba hali"</string> @@ -233,25 +235,25 @@ <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Inaruhusu programu kupanua au kukunja upau wa hali."</string> <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"panga upya simu zinazotoka"</string> <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Inaruhusu programu kuchakata simu zinazotoka nje na kubadilisha nambari ya kupigwa. Idhini hii inaruhusu programu kuchunguza, kuelekeza upya, au kuzuia simu zinazotoka nje."</string> - <string name="permlab_receiveSms" msgid="8673471768947895082">"pokea jumbe za maandishi (SMS)"</string> + <string name="permlab_receiveSms" msgid="8673471768947895082">"pokea ujumbe wa maandishi wa SMS"</string> <string name="permdesc_receiveSms" msgid="6424387754228766939">"Inaruhusu programu kupokea na kuchakata ujumbe wa SMS. Hii inamaanisha programu hii inaweza kuchunguza na kufuta ujumbe uliotumwa katika kifaa chako bila ya kukuonyesha."</string> - <string name="permlab_receiveMms" msgid="1821317344668257098">"pokea jumbe za maandishi (MMS)"</string> + <string name="permlab_receiveMms" msgid="1821317344668257098">"pokea ujumbe wa maandishi wa MMS"</string> <string name="permdesc_receiveMms" msgid="533019437263212260">"Inaruhusu programu kupokea na kuchakata ujumbe medianwai (MMS). Hii inamaanisha uwezo wa kuchunguza na kufuta ujumbe uliotumwa kwa kifaa chako bila ya kukuonyesha."</string> <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"Pokea matangazo ya dharura"</string> - <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Inaruhusu programu kupokea na kuchakata jumbe za dharura. Ruhusa hii inapatikana tu kwa programu za mfumo."</string> - <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"soma jumbe za matangazo ya simu"</string> - <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Huruhusu programu kusoma jumbe za matangazo ya simu zilizopokewa na kifaa chako. Taarifa za matangazo ya simu huwasilishwa katika maeneo mengine ili kukuonya juu ya hali za dharura. Huenda programu hasidi zikatatiza utendajikazi au shughuli ya kifaa chako wakati matangazo ya simu ya dharura yamepokewa."</string> + <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Huruhusu programu kupokea na kuchakata mawasiliano ya dharura. Idhini hii inapatikana tu kwa programu za mfumo."</string> + <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"soma mawasiliano ya matangazo ya simu"</string> + <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Huruhusu programu kusoma mawasiliano ya matangazo ya simu yaliyoingia kwenye kifaa chako. Arifa za matangazo ya simu huwasilishwa katika maeneo mengine ili kukuonya juu ya hali za dharura. Huenda programu hasidi zikatatiza utendajikazi au shughuli ya kifaa chako wakati matangazo ya simu ya dharura yameingia."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"tuma ujumbe wa SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Inaruhusu programu kutuma ujumbe wa SMS. Hii inaweza ikasababisha malipo yasiyotarajiwa. Programu hasidi zinaweza kukugharimu pesa kwa kutuma ujumbe bila uthibitisho wako."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"Tuma ujumbe wa SMS bila ya thibitisho"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Inaruhusu programu kutuma ujumbe wa SMS. Hii inaweza ikasababisha malipo yasiyotarajiwa. Programu hasidi zinaweza kukugharimu pesa kwa kutuma ujumbe bila uthibitisho wako."</string> - <string name="permlab_readSms" msgid="8745086572213270480">"soma jumbe zako za maandishi (SMS au MMS)"</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"tuma matukio ya kujibu-kupitia-ujumbe"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Inaruhusu programu kutuma maombi kwa programu nyingine za ujumbe ili kushughulikia matukio ya kujibu-kupitia-ujumbe kwa simu zinazoingia."</string> + <string name="permlab_readSms" msgid="8745086572213270480">"soma SMS au MMS zako"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Inaruhusu programu kusoma ujumbe wa SMS uliohifadhiwa kwenye kompyuta kibao yako au SIM kadi. Hii inaruhusu programu kusoma ujumbe wote wa SMS, bila kujali maudhui au usiri."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Inaruhusu programu kusoma ujumbe wa SMS uliohifadhiwa kwenye simu yako au SIM kadi. Hii inaruhusu programu kusoma ujumbe wote wa SMS, bila kujali maudhui au usiri."</string> - <string name="permlab_writeSms" msgid="3216950472636214774">"Hariri jumbe zako za maandishi (SMS au MMS)"</string> - <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"Inaruhusu programu kuandikia jumbe za SMS zinazohifadhiwa kwenye kompyuta yako kibao au SIM kadi. Programu hasidi zinaweza kufuta jumbe zako."</string> - <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"Inaruhusu programu kuandika jumbe za SMS zinazohifadhiwa kwenye simu yako au SIM kadi. programu hasidi zinaweza kufuta ujumbe zako."</string> - <string name="permlab_receiveWapPush" msgid="5991398711936590410">"pokea jumbe za maandishi (WAP)"</string> + <string name="permlab_writeSms" msgid="3216950472636214774">"Hariri SMS au MMS zako"</string> + <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"Huruhusu programu kuandikia SMS zinazohifadhiwa kwenye kompyuta yako kibao au SIM kadi. Programu hasidi zinaweza kufuta SMS zako."</string> + <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"Huruhusu programu kuandika SMS zinazohifadhiwa kwenye simu yako au SIM kadi. programu hasidi zinaweza kufuta SMS zako."</string> + <string name="permlab_receiveWapPush" msgid="5991398711936590410">"pokea ujumbe wa maandishi wa WAP"</string> <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Inaruhusu programu kupokea na kuchakata ujumbe wa WAP. Idhini hii inajumuisha uwezo wa kuchunguza na kufuta ujumbe uliotumwa kwako bila ya kukuonyesha."</string> <string name="permlab_getTasks" msgid="6466095396623933906">"rudisha programu zinazoendeshwa"</string> <string name="permdesc_getTasks" msgid="7454215995847658102">"Inaruhusu programu kurudisha taarifa kuhusu kazi zinazoendeshwa sasa na hivi karibuni. Hii inaweza kuruhusu programu kugundua taarifa kuhusu ni programu zipi zinazotumika kwenye kifaa."</string> @@ -299,12 +301,14 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Huweka kisimamia shughuli katika hali ya kuzima. Haiadhiri uzimaji kamili"</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zuia swichi za app"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Huzuia mtumiaji dhidi ya kubadilisha na kwenda kwa programu nyingine."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pata maelezo ya sasa kuhusu programu"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Huruhusu mmiliki kurejesha maelezo ya faragha kuhusu programu ya sasa katika mandharimbele ya skrini."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"Fuatilia na kudhibiti uzinduzi wote wa programu"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Inaruhusu programu kufuatilia na kudhibiti jinsi mfumo unazindua shughuli. Programu hasidi zinaweza kutia mfumo hatarini. Ruhusa inahitajika tu kwa usanidi, kamwe sio kwa matumizi ya kawaida."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"furushi lililotumwa limeondoa tangazo"</string> <string name="permdesc_broadcastPackageRemoved" msgid="6621901216207931089">"Inaruhusu programu kutangaza taarifa kwamba furushi ya programu imetolewa. Programu hasidi zinaweza kutumia hii kuua programu yoyote inayoendeshwa."</string> <string name="permlab_broadcastSmsReceived" msgid="5689095009030336593">"tuma matanazo yaliyopokewa ya SMS"</string> - <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Inaruhusu programu kutangaza taarifa kwamba ujumbe wa SMS umepokewa. Programu hasidi zinaweza tumia hii kubuni jumbe za SMS zinazoingia."</string> + <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Huruhusu programu kutangaza taarifa kwamba ujumbe wa SMS umeingia. Programu hasidi zinaweza kutumia hii kubuni SMS zinazoingia."</string> <string name="permlab_broadcastWapPush" msgid="3145347413028582371">"tuma tangazo lililopokewa la MSUKUMO WA WAP"</string> <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Inaruhusu programu kutangaza taarifa kwamba ujumbe wa WAP PUSH umepokewa. Programu hasidi zinaweza kutumia hii kubuni risiti ya ujumbe wa MMS au polepole kubadilisha maudhui yoyote ya ukurasa wa tovuti na vibadala vibovu."</string> <string name="permlab_setProcessLimit" msgid="2451873664363662666">"zuia idadi ya michakato inayoendeshwa"</string> @@ -315,7 +319,11 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Inaruhusu programu kusoma data ya sasa ya matumizi ya kiwango cha chini cha betri. Huenda ikaruhusu kupata maelezo ya kina kuhusu programu unazozitumia."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rekebisha takwimu za betri"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Inaruhusu programu kurekebisha takwimu za betri zilizokusanywa. Si ya kutumiwa na programu za kawaida."</string> - <string name="permlab_backup" msgid="470013022865453920">"Dhibiti chelezo la mfumo na rejesha"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"epua takwimu za oparesheni ya programu"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Inaruhusu programu kuepua takwimu za matumizi ya programu zilizokusanywa. Si ya kutumiwa na programu za kawaida."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"rekebisha takwimu za oparesheni ya programu"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Inaruhusu programu kurekebisha takwimu za matumizi ya programu zilizokusanywa. Si ya kutumiwa na programu za kawaida."</string> + <string name="permlab_backup" msgid="470013022865453920">"Dhibiti kuhifadhi nakala na kurejesha kwa mfumo"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Inaruhusu programu kudhibiti utaratibu wa kucheleza na kurejesha wa mfumo. Si kwa matumizi na programu za kawaida."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"thibitisha chelezo kamilifu au rejesha upya uendeshaji"</string> <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Inaruhusu programu kuzindua UI ya kuthibitisha chelezo kamili. Si ya kutumiwa na programu yoyote."</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Inaruhusu mmiliki kushurutisha kwenye kusano ya kiwango cha juu ya mbinu ya ingizo. Haipaswi kuhitajika kwa programu za kawaida."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"funga kwa huduma ya ufikiaji"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Inamuruhusu mmiliki kufunga kipengee kinachojitokeza katika nyanja mbalimbali za kiwango cha juu cha huduma ya afikiaji. Hapaswi kuhitajika kwa programu za kawaida."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"omba uchunguzi kwa kugusa"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Huruhusu programu kuomba hali ya mwingiliano ambapo vipengee vilivyoguswa hutamkwa kwa sauti na Kiolesura kinaweza kuchunguzwa kupitia ishara."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"omba ufikiaji wa wavuti ulioimarishwa"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Huruhusu programu kuomba uwezeshaji wa uimarishaji wa ufikiaji wa wavuti. Kwa mfano, kusakinisha hati ili kufanya maudhui ya programu kufikiwa zaidi."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"Imefungwa kwa huduma ya maandishi"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Inaruhusu kishikiliaji kushurutisha kusano ya kiwango cha juu ya huduma ya matini(k.m.SpellCheckerService). Haipaswi kuhitajika kwa programu za kawaida."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"funga kwa huduma ya VPN"</string> @@ -417,9 +429,9 @@ <string name="permlab_writeProfile" msgid="907793628777397643">"rekebisha kadi yako mwenyewe ya mawasiliano"</string> <string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Inaruhusu programu kubadilisha au kuongeza taarifa ya maelezo mafupi ya kibinafsi yaliyohifadhiwa kwenye kifaa chako, kama vile jina lako na taarifa ya anwani. Hii inamaanisha kuwa programu inaweza kukutambua na inaweza kutuma taarifa ya maelezo yako mafupi kwa wengine."</string> <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"soma mipasho yako wa kijamii"</string> - <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Inaruhusu programu kufikia na kupatanisha sasisho za kijamii kutoka kwa marafiki zako. Kuwa makini wakati unashiriki taarifa -- hii inaruhusu programu kusoma mawasiliano kati yako na marafiki zako kwenye mitandao ya jamii, bila kujali usiri. Kumbuka: idhini hii haiwezi kutekelezwa kwenye mitandao yote ya jamii."</string> + <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Huruhusu programu kufikia na kupatanisha masasisho ya kijamii kutoka kwa marafiki zako. Kuwa makini wakati unashiriki taarifa -- hii huruhusu programu kusoma mawasiliano kati yako na marafiki zako kwenye mitandao ya jamii, bila kujali usiri. Kumbuka: idhini hii haiwezi kutekelezwa kwenye mitandao yote ya jamii."</string> <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"andika kwa mipasho yako wa kijamii"</string> - <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Inaruhusu programu kuonyesha sasisho za kijamii kutoka kwa marafiki zako. Kuwa makini wakati unashiriki taarifa -- hii inaruhusu programu kutoa ujumbe unaoweza kuonekana kuwa unatoka kwa rafiki. Kumbuka: idhini hii huenda usitekelezwe kwenye mitandao yote ya kijamii."</string> + <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Huruhusu programu kuonyesha masasisho ya kijamii kutoka kwa marafiki zako. Kuwa makini wakati unashiriki taarifa -- hii inaruhusu programu kutoa ujumbe unaoweza kuonekana kuwa unatoka kwa rafiki. Kumbuka: idhini hii huenda usitekelezwe kwenye mitandao yote ya jamii."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"soma matukio ya kalenda pamoja na maelezo ya siri"</string> <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Inaruhusu programu kusoma matukio yote ya kalenda yaliohifadhiwa kwenye kompyuta kibao yako, yakijumuisha yale ya marafiki au wafanyakazi wenza. Hii inaweza kuruhusu programu kushiriki au kuhifadhi data yako ya kaelnda, bila kujali usiri au unyeti."</string> <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Inaruhusu programu kusoma matukio yote ya kalenda yaliyohifadhiwa kwenye simu yako, pamoja na yale ya marafiki au wafanyakazi wenza. Hii inaweza kuruhusu programu kushiriki au kuhifadhi data yako ya kalenda, bila kujali usiri au umuhimu."</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Inaruhusu programu kudhibiti sera za mtandao na kufafanua sheria maalum za programu."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"badilisha uthibitishaji wa matumizi ya mtandao"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Huruhusu programu kurekebisha jinsi matumizi ya mtandao yana hesabika dhidi ya programu. Sio ya matumizi na programu za kawaida."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"fikia arifa"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Huruhusu programu kurejesha, kuchunguza, na kuondoa arifa, ikiwa ni pamoja na zile zilizochapishwa na programu nyingine."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Weka kanuni za nenosiri"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Dhibiti urefu na vibambo vinavyoruhusiwa katika manenosiri ya kufungua skrini."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Chunguza majaribio ya kutofun gua skrini"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Ruwaza imefutwa"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Kiini kimeongezwa"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Ruwaza imekamilika"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Wiji %2$d ya %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Ongeza wiji"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Tupu"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Eneo la kufungua limepanuliwa."</string> @@ -848,8 +863,8 @@ <string name="js_dialog_before_unload" msgid="730366588032430474">"Toka kwa ukurasa huu?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Gusa Sawa ili kuendelea, au Ghairi ili kubaki kwenye ukurasa wa sasa."</string> <string name="save_password_label" msgid="6860261758665825069">"Thibitisha"</string> <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gonga mara mbili ili kukuza ndani na nje."</string> - <string name="autofill_this_form" msgid="4616758841157816676">"Mjazo-otomatiki"</string> - <string name="setup_autofill" msgid="7103495070180590814">"Sanidi Mjazo-otomati"</string> + <string name="autofill_this_form" msgid="4616758841157816676">"Kujaza kiotomatiki"</string> + <string name="setup_autofill" msgid="7103495070180590814">"Weka uwezo wa kujaza kiotomatiki"</string> <string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string> <string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> @@ -870,11 +885,11 @@ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Inaruhusu programu kusoma historia ya URL zote ambazo zimetembelewa na Kivinjari, na alamisho zote za Kivinjari. Kumbuka: idhini hii haiwezi kutekelezwa vivinjari vya vingine au programu zingine zenye uwezo wa kuvinjari."</string> <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"andika alamisho na historia ya wavuti"</string> <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Inaruhusu programu kurekebisha historia ya Kivinjari au alamisho zilizohifadhiwa kwenye kompyuta kibao yako. Hii inaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na kivinjari kingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string> - <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Inaruhusu programu kurekebisha historia ya Kivinjari au vialamisho vilivyohifadhiwa kwenye simu yako. Hii huenda ikaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na vivinjari vingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string> + <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Inaruhusu programu kurekebisha historia ya Kivinjari au alamisho zilizohifadhiwa kwenye simu yako. Hii huenda ikaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na vivinjari vingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string> <string name="permlab_setAlarm" msgid="1379294556362091814">"weka kengele"</string> <string name="permdesc_setAlarm" msgid="316392039157473848">"Inaruhusu programu kuweka kengele katika programu iliyosakinishwa ya kengele. Programu zingine za kengele zinawezakosa kutekeleza kipengee hiki."</string> <string name="permlab_addVoicemail" msgid="5525660026090959044">"ongeza barua ya sauti"</string> - <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Inaruhusu programu kuongeza jumbe kwenye kikasha cha ujumbe wa sauti."</string> + <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Huruhusu programu kuongeza mawasiliano kwenye kikasha cha ujumbe wa sauti."</string> <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Rekebisha vibali vya Kivinjari cha eneo la jio"</string> <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Inaruhusu programu kurekebisha ruhusa za eneo la jio za kivinjari. Programu hasidi zinaweza tumia hii kuruhusu kutuma taarifa ya eneo kwa wavuti holela."</string> <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"thibitisha furushi"</string> @@ -1127,7 +1142,7 @@ <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Simu itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Ingiza kibambo"</string> <string name="sms_control_title" msgid="7296612781128917719">"Inatuma ujumbe wa SMS"</string> - <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> inatuma idadi kubwa ya jumbe za SMS. Je, unataka kuruhusu programu hii kuendelea kutuma jumbe?"</string> + <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> inatuma idadi kubwa ya SMS. Je, unataka kuruhusu programu hii kuendelea kutuma SMS?"</string> <string name="sms_control_yes" msgid="3663725993855816807">"Ruhusu"</string> <string name="sms_control_no" msgid="625438561395534982">"Kataza"</string> <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ingependa kutuma ujumbe kwa <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 38bf592..af28134 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"พื้นที่เก็บข้อมูล"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"เข้าถึงที่เก็บข้อมูล USB"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"เข้าถึงการ์ด SD"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"คุณลักษณะการเข้าถึง"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"คุณลักษณะที่เทคโนโลยีความช่วยเหลือสามารถร้องขอได้"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"ปิดการใช้งานหรือแก้ไขแถบสถานะ"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"อนุญาตให้แอปพลิเคชันปิดใช้งานแถบสถานะหรือเพิ่มและนำไอคอนระบบออก"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"แถบสถานะ"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"อนุญาตให้แอปอ่านข้อความจากสถานีมือถือที่อุปกรณ์ได้รับ การแจ้งเตือนทางมือถือมีให้บริการในบางพื้นที่ โดยจะแจ้งเตือนคุณเกี่ยวกับสถานการณ์ฉุกเฉิน แอปที่เป็นอันตรายอาจเข้าแทรกแซงการทำงานของอุปกรณ์เมื่อได้รับข้อความแจ้งเตือนฉุกเฉิน"</string> <string name="permlab_sendSms" msgid="5600830612147671529">"ส่งข้อความ SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"อนุญาตให้แอปพลิเคชันส่งข้อความ SMS ซึ่งอาจทำให้มีการเรียกเก็บเงินที่ไม่คาดคิด แอปพลิเคชันที่เป็นอันตรายอาจทำให้คุณเสียค่าใช้จ่ายด้วยการส่งข้อความโดยไม่รอการยืนยันจากคุณ"</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"ส่งข้อความ SMS โดยไม่มีการยืนยัน"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"อนุญาตให้แอปพลิเคชันส่งข้อความ SMS ซึ่งอาจทำให้มีการเรียกเก็บเงินที่ไม่คาดคิด แอปพลิเคชันที่เป็นอันตรายอาจทำให้คุณเสียค่าใช้จ่ายด้วยการส่งข้อความโดยไม่รอการยืนยันจากคุณ"</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"ส่งกิจกรรมการตอบสนองผ่านทางข้อความ"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"อนุญาตให้แอปพลิเคชันส่งคำขอไปยังแอปพลิเคชันการรับส่งข้อความอื่นๆ ในการจัดการกิจกรรมการตอบสนองผ่านทางข้อความสำหรับสายเรียกเข้า"</string> <string name="permlab_readSms" msgid="8745086572213270480">"อ่านข้อความของคุณ (SMS หรือ MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"อนุญาตให้แอปพลิเคชันอ่านข้อความ SMS ที่จัดเก็บไว้ในแท็บเล็ตหรือซิมการ์ด ซึ่งจะทำให้แอปพลิเคชันสามารถอ่านข้อความ SMS ทั้งหมดได้ไม่ว่าจะเป็นเนื้อหาใดหรือมีการรักษาข้อมูลที่เป็นความลับแบบใด"</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"อนุญาตให้แอปพลิเคชันอ่านข้อความ SMS ที่จัดเก็บไว้ในโทรศัพท์หรือซิมการ์ด ซึ่งจะทำให้แอปพลิเคชันสามารถอ่านข้อความ SMS ทั้งหมดได้ไม่ว่าจะเป็นเนื้อหาใดหรือมีการรักษาข้อมูลที่เป็นความลับแบบใด"</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"กำหนดให้ตัวจัดการกิจกรรมอยู่ในสถานะปิดระบบ โดยไม่ได้ปิดระบบอย่างสมบูรณ์"</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ป้องกันการเปลี่ยนแอปพลิเคชัน"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"ป้องกันไม่ให้ผู้ใช้สลับไปใช้แอปพลิเคชันอื่น"</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"รับข้อมูลแอปพลิเคชันปัจจุบัน"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"ช่วยให้เจ้าของสามารถดึงข้อมูลส่วนตัวเกี่ยวกับแอปพลิเคชันปัจจุบันในส่วนหน้าของหน้าจอ"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"ตรวจสอบและควบคุมแอปพลิเคชันทั้งหมดที่เปิดใช้งาน"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"อนุญาตให้แอปพลิเคชันตรวจสอบและควบคุมวิธีการที่ระบบเปิดกิจกรรมต่างๆ แอปพลิเคชันที่เป็นอันตรายอาจทำอันตรายแก่ระบบได้อย่างสิ้นเชิง การอนุญาตนี้จำเป็นสำหรับการพัฒนาเท่านั้น ไม่ใช้สำหรับแอปพลิเคชันทั่วไปโดยเด็ดขาด"</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"ส่งการกระจายข้อมูลว่ามีการนำแพคเกจออก"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"อนุญาตให้แอปพลิเคชันอ่านข้อมูลการใช้แบตเตอรี่ที่มีพลังงานเหลือน้อยในปัจจุบัน โดยอาจอนุญาตให้แอปพลิเคชันค้นหาข้อมูลรายละเอียดว่าคุณใช้งานแอปพลิเคชันใดบ้าง"</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"แก้ไขสถิติของแบตเตอรี่"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"อนุญาตให้แอปพลิเคชันแก้ไขสถิติของแบตเตอรี่่ที่เก็บรวบรวมไว้ ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"เรียกคืนสถิติการทำงานของแอปพลิเคชัน"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"อนุญาตให้แอปพลิเคชันเรียกคืนสถิติการทำงานของแอปพลิเคชันที่เก็บรวบรวมไว้ ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"แก้ไขสถิติการทำงานของแอปพลิเคชัน"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"อนุญาตให้แอปพลิเคชันแก้ไขสถิติการทำงานของแอปพลิเคชันที่เก็บรวบรวมไว้ ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string> <string name="permlab_backup" msgid="470013022865453920">"ควบคุมการสำรองและคืนค่า"</string> <string name="permdesc_backup" msgid="6912230525140589891">"อนุญาตให้แอปพลิเคชันควบคุมการสำรองข้อมูลของระบบและกลไกการเรียกคืน ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"ยืนยันการสำรองข้อมูลหรือการคืนค่าทั้งหมด"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของวิธีการป้อนข้อมูล ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"เชื่อมโยงกับบริการการเข้าถึง"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"อนุญาตให้เจ้าของเชื่อมโยงกับส่วนติดต่อระดับบนสุดของบริการการเข้าถึง ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"ขอการแตะเพื่อสำรวจ"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"อนุญาตให้ผู้ถือสิทธิ์ขอโหมดโต้ตอบซึ่งจะมีเสียงพูดออกมาเมื่อรายการถูกแตะ และสามารถสำรวจ UI ได้โดยการใช้ท่าทางสัมผัส"</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"ขอการเข้าถึงเว็บที่มีประสิทธิภาพมากขึ้น"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"อนุญาตให้ผู้ถือสิทธิ์ขอเปิดใช้การเพิ่มประสิทธิภาพการเข้าถึงเว็บ ตัวอย่างเช่น การติดตั้งสคริปต์เพื่อให้เข้าถึงเนื้อหาแอปพลิเคชันได้ดีขึ้น"</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"เชื่อมโยงกับบริการข้อความ"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของบริการข้อความ (เช่น บริการเครื่องตรวจตัวสะกด) ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"เชื่อมโยงกับบริการ VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"อนุญาตให้แอปพลิเคชันจัดการนโยบายเครือข่ายและกำหนดกฎเฉพาะแอปพลิเคชัน"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"แก้ไขการบันทึกบัญชีการใช้งานเครือข่าย"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"อนุญาตให้แอปพลิเคชันแก้ไขวิธีการบันทึกบัญชีการใช้งานเครือข่ายของแอปพลิเคชัน ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"เข้าถึงการแจ้งเตือน"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกดู ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"ตั้งค่ากฎรหัสผ่าน"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"ควบคุมความยาวและอักขระที่อนุญาตให้ใช้ในรหัสผ่านการปลดล็อกหน้าจอ"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"ล้างรูปแบบแล้ว"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"เพิ่มเซลแล้ว"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"วาดรูปแบบเสร็จสิ้น"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s วิดเจ็ต %2$d ของ %3$d"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"เพิ่มวิดเจ็ต"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"ว่าง"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"ขยายพื้นที่ปลดล็อกแล้ว"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index e3260db..6268786 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Imbakan"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"I-access ang imbakan na USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"I-access ang SD card."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Mga tampok ng accessibility"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Mga tampok na maaaring hilingin ng tumutulong na teknolohiya."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"huwag paganahin o baguhin ang status bar"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Pinapayagan ang app na huwag paganahin ang status bar o magdagdag at mag-alis ng mga icon ng system."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"status bar"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Binibigyang-daan ang app na magbasa ng mga mensahe ng cell broadcast na natanggap ng iyong device. Inihahatid ang mga alerto ng cell broadcast sa ilang lokasyon upang balaan ka tungkol sa mga emergency na sitwasyon. Maaaring makaabala ang nakakahamak na apps sa pagganap o pagpapatakbo ng iyong device kapag nakatanggap ng emergency na cell broadcast."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"magpadala ng mga SMS na mensahe"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Pinapayagan ang app na magpadala ng mga mensaheng SMS. Maaari itong magresulta sa mga hindi inaasahang pagsingil. Maaaring magpagastos sa iyo ng pera ang nakakahamak na apps sa pamamagitan ng pagpapadala ng mga mensahe nang wala ng iyong kumpirmasyon."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"magpadala ng mga SMS na mensahe nang walang pagkumpirma"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Pinapayagan ang app na magpadala ng mga mensaheng SMS. Maaari itong magresulta sa mga hindi inaasahang pagsingil. Maaaring magpagastos sa iyo ng pera ang nakakahamak na apps sa pamamagitan ng pagpapadala ng mga mensahe nang wala ng iyong kumpirmasyon."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"magpadala ng mga kaganapan ng tumugon sa pamamagitan ng mensahe"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Binibigyang-daan ang app na magpadala ng mga kahilingan sa iba pang apps ng pagmemensahe upang pangasiwaan ang mga kaganapan ng tumugon sa pamamagitan ng mensahe para sa mga papasok na tawag."</string> <string name="permlab_readSms" msgid="8745086572213270480">"basahin ang iyong mga text message (SMS o MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Pinapayagan ang app na magbasa ng mga mensaheng SMS na naka-imbak sa iyong tablet o SIM card. Pinapayagan nito ang app na basahin ang lahat ng mensaheng SMS, ano pa man ang nilalaman at katayuan sa pagiging kumpedensyal ng mga ito."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Pinapayagan ang app na magbasa ng mga mensaheng SMS na naka-imbak sa iyong telepono o SIM card. Pinapayagan nito ang app na basahin ang lahat ng mensaheng SMS, ano pa man ang nilalaman at katayuan sa pagiging kumpedensyal ng mga ito."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Inilalagay ang tagapamahala ng aktibidad sa katayuan ng pag-shutdown. Hindi nagsasagawa ng kumpletong pag-shutdown."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"pigilan ang mga paglipat ng app"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Pinipigilan ang mga user sa paglipat sa isa pang app."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"kunin ang impormasyon ng kasalukuyang app"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Pinapayagan ang may-ari na bawiin ang pribadong impormasyon tungkol sa kasalukuyang application sa foreground ng screen."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"subaybayan at kontrolin ang lahat ng paglunsad ng app"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Pinapayagan ang app na subaybayan at kontrolin kung paano naglulunsad ng mga aktibidad ang system. Maaaring ganap na ikompromiso ng nakakahamak na apps ang system. Kinakailangan lamang ang pahintulot na ito para sa pagpapabuti, hindi kailanman para sa normal na paggamit."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"magpadala ng package inalis ang broadcast"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Binibigyang-daan ang application na basahin ang kasalukuyang data sa paggamit ng mababang antas ng baterya. Maaaring bigyang-daan ang application na malaman ang detalyadong impormasyon tungkol sa kung aling apps ang ginagamit mo."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"baguhin ang mga istatistika ng baterya"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Binibigyang-daan ang app na baguhin ang mga nakolektang istatistika ng baterya. Hindi para sa paggamit ng normal na apps."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"bawiin ang mga istatistika ng pagpapagana ng app"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Binibigyang-daan ang app na bawiin ang mga nakolektang istatistika ng pagpapagana ng application. Hindi para sa paggamit ng normal na apps."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"baguhin ang mga istatistika ng pagpapatakbo ng app"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Binibigyang-daan ang app na baguhin ang mga nakolektang istatistika ng pagpapatakbo ng application. Hindi para sa paggamit ng normal na apps."</string> <string name="permlab_backup" msgid="470013022865453920">"kontrolin ang system backup at pagbawi"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Pinapayagan ang app na kontrolin ang backup ng system at ipanumbalik ang mekanismo. Hindi para sa paggamit ng normal na apps."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Kumpirmahin ang isang buong pagpapatakbo ng pag-backup o pagpapanumbalik"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng pamamaraan ng pag-input. Hindi kailanman dapat na kailanganin para sa normal na apps."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sumailalim sa isang serbisyo sa accessibility"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Binibigyang-daan ang may-ari na sumailalim sa nasa nangungunang antas na interface ng isang serbisyo sa accessibility. Hindi dapat kailanman kailanganin para sa normal na apps."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"humiling ng explore by touch"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Binibigyang-daan ang may-ari na humiling ng mode ng pakikipag-ugnayan kung saan sinasabi nang malakas ang mga napindot na item at nagagalugad ang UI sa pamamagitan ng mga galaw."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"humiling ng pinahusay na accessibility sa web"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Binibigyang-daan ang may-ari na hilingin ang pagpapagana ng mga pagpapahusay sa accessibility sa web. Halimbawa, ang pag-install ng mga script mula sa Google upang gawing mas naa-access ang nilalaman ng app."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"sumailalim sa serbisyo ng teksto"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Pinapayagan ang may-hawak na sumailalim sa nangungunang antas na interface (hal. SpellCheckerService). Hindi kailanman dapat na kailanganin para sa normal na apps."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"sumailalim sa isang serbisyo ng VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Pinapayagan ang app na pamahalaan ang mga patakaran ng network at ilarawan ang mga patakarang tukoy sa app."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"baguhin ang pagkukuwenta sa paggamit ng network"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Pinapayagan ang app na baguhin kung paano isinasaalang-alang ang paggamit ng network laban sa apps. Hindi para sa paggamit ng normal na apps."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"i-access ang mga notification"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Pinapayagan ang app na kumuha, sumuri, at mag-clear ng mga notification, kabilang ang mga na-post ng iba pang apps."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Magtakda ng mga panuntunan sa password"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolin ang haba at mga character na pinapayagan sa mga password sa pag-unlock ng screen."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Na-clear ang pattern"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Idinagdag ang cell"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Nakumpleto ang pattern"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d ng %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Magdagdag ng widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Walang laman"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Pinalaki ang bahagi ng pag-unlock."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 7940089..21fe6d4 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Depolama"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"USB belleğe erişin."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SD karta erişin."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Erişilebilirlik özellikleri"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Engelli kullanıcılara yardımcı olan teknolojinin istekte bulunabileceği özellikler."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"durum çubuğu"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Uygulamaya, cihazınız tarafından alınan hücre yayını mesajlarını okuma izni verir. Hücre yayını uyarıları bazı yerlerde acil durumlar konusunda sizi uyarmak için gönderilir. Kötü amaçlı uygulamalar acil hücre yayını alındığında cihazınızın performansına ya da çalışmasına engel olabilir."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"SMS mesajları gönder"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Uygulamaya SMS mesajları gönderme izni verir. Bu durum beklenmeyen ödemelere neden olabilir. Kötü amaçlı uygulamalar onayınız olmadan mesajlar göndererek sizi zarara uğratabilir."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"SMS mesajlarını onaysız gönder"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Uygulamaya SMS mesajları gönderme izni verir. Bu durum beklenmeyen ödemelere neden olabilir. Kötü amaçlı uygulamalar onayınız olmadan mesajlar göndererek sizi zarara uğratabilir."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"mesajla yanıtla etkinlikleri gönder"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Uygulamaya, gelen çağrıları mesajla yanıtlama etkinliklerini işlemek üzere diğer mesajlaşma uygulamalarına istek gönderme izni verir."</string> <string name="permlab_readSms" msgid="8745086572213270480">"kısa mesajlarımı (SMS veya MMS) oku"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Uygulamaya tabletinizde veya SIM kartta saklanan SMS mesajlarını okuma izni verir. Bu izin, uygulamanın tüm SMS mesajlarını içeriğinden veya gizliliğinden bağımsız olarak okumasına olanak sağlar."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Uygulamaya telefonunuzda veya SIM kartta saklanan SMS mesajlarını okuma izni verir. Bu izin, uygulamanın tüm SMS mesajlarını içeriğinden veya gizliliğinden bağımsız olarak okumasına olanak sağlar."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Eylem yöneticisini kapalı duruma getirir. Tam kapatma işlemi gerçekleştirmez."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"uygulama değişimlerini engelle"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Kullanıcının başka bir uygulamaya geçiş yapmasını engeller."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"geçerli uygulama bilgilerini al"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"İzin sahibine, ekranın ön planındaki geçerli uygulama hakkında gizli bilgileri alma olanağı verir."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"tüm uygulama başlatma işlemlerini izle ve denetle"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Uygulamaya, sistemin etkinlikleri nasıl başlattığını izleme ve denetleme izni verir. Kötü amaçlı uygulamalar sistemi tamamen tehlikeye atabilir. Bu izin normal kullanım için değildir, sadece geliştirme süreçlerinde kullanılır."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"paket ile kaldırılan yayını gönder"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Uygulamaya, mevcut pil kullanım verilerini alt düzeyde okuma izni verir. Uygulamanın hangi uygulamaları kullandığınızla ilgili ayrıntılı bilgi edinmesine olanak sağlayabilir."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"pil istatistiklerini değiştir"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Uygulamaya, toplanan pil kullanım istatistiklerini değiştirme izni verir. Normal uygulamaların kullanımına yönelik değildir."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"uygulama çalışma istatistiklerini al"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Uygulamaya, uygulama çalışma istatistiklerini alma izni verir. Normal uygulamaların kullanımına yönelik değildir."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"uygulama çalışma istatistiklerini değiştir"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Uygulamaya, uygulama çalışma istatistiklerini değiştirme izni verir. Normal uygulamaların kullanımına yönelik değildir."</string> <string name="permlab_backup" msgid="470013022865453920">"sistem yedeğini kontrol et ve geri yükle"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Uygulamaya, sistem yedekleme ve geri yükleme mekanizmasını denetleme izni verir. Normal uygulamaların kullanımına yönelik değildir."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"tam yedekleme veya geri yükleme işlemini onaylayın"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Cihazın sahibine, bir giriş yönteminin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"erişilebilirlik hizmetine bağlan"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"İzin sahibine bir erişilebilirlik hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"dokunarak keşfetme isteğinde bulunur"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"İzin sahibine, dokunulan öğelerin sesli okunduğu ve kullanıcı arayüzünün hareketlerle keşfedilebildiği etkileşimli bir mod isteğinde bulunma olanağı sağlar."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"genişletilmiş Web erişilebilirliği isteğinde bulunur"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"İzin sahibine Web erişim geliştirmelerini etkinleştirme isteğinde bulunma olanağı sağlar. Örneğin, uygulama içeriğinin daha fazla erişilebilir olması için Google\'dan komut dosyası yüklemek gibi."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"kısa mesaj hizmetine bağla"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Cihazın sahibine, bir metin hizmetinin (ör. SpellCheckerService) en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerekmez."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"VPN hizmetine bağlan"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Uygulamaya, ağ politikalarını yönetme ve uygulamaya özgü kuralları tanımlama izni verir."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ağ kullanım hesaplamasını değiştir"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Uygulamaya, ağın uygulamalara göre nasıl kullanılacağını değiştirme izni verir. Normal uygulamalar tarafından kullanılmak için değildir."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"bildirimlere eriş"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Uygulamanın bildirimler almasına, bildirimleri incelemesine ve temizlemesine izin verir. Buna diğer uygulamalar tarafından yayınlanan bildirimler de dahildir."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Şifre kuralları ayarla"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Ekran kilidini açma şifrelerinde izin verilen uzunluğu ve karakterleri denetleme."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Ekran kilidini açma denemelerini izle"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Desen temizlendi"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Hücre eklendi"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Desen tamamlandı"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d / %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Widget ekleyin."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Boş"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Kilit açma alanı genişletildi."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 7e5d711..ce5fdc0 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Зберігання"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Отрим. доступу до носія USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Доступ до карти SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Функції доступності"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Функції, на які може подавати запит допоміжна технологія."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"вимикати чи змін. рядок стану"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволяє програмі вимикати рядок стану чи додавати та видаляти піктограми системи."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"рядок стану"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Дозволяє програмі читати широкомовні повідомлення мережі, отримані пристроєм. Широкомовні сповіщення мережі надсилаються в деяких країнах для попередження про надзвичайні ситуації. Шкідливі програми можуть втручатися у швидкодію чи роботу пристрою під час отримання широкомовного повідомлення мережі про надзвичайну ситуацію."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"надсил. SMS повідом."</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Дозволяє програмі надсилати SMS-повідомлення. Це може спричинити неочікуване стягнення плати. Шкідливі програми можуть надсилати повідомлення без вашого підтвердження, за що з вас стягуватимуться кошти."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"надсилати SMS-повідомлення без підтвердження"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Дозволяє програмі надсилати SMS-повідомлення. Це може спричинити неочікуване стягнення плати. Шкідливі програми можуть надсилати повідомлення без вашого підтвердження, за що з вас стягуватимуться кошти."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"надсилати події типу \"відповідь повідомленням\""</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Дозволяє програмі надсилати запити іншим програмам обміну повідомленнями, щоб обробляти події типу \"відповідь повідомленням\" для вхідних викликів."</string> <string name="permlab_readSms" msgid="8745086572213270480">"читати текстові повідомлення (SMS або MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Дозволяє програмі читати SMS повідомлення, збережені в планшетному ПК чи на SIM-карті. Це дозволяє програмі читати всі SMS повідомлення, незалежно від вмісту чи конфіденційності."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Дозволяє програмі читати SMS повідомлення, збережені в телефоні чи на SIM-карті. Це дозволяє програмі читати всі SMS повідомлення, незалежно від вмісту чи конфіденційності."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Переводить диспетчер дій у стан завершення роботи. Не виконує повне завершення роботи."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"запобіг. зміні програм"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Запобігати переходу користувача до іншої програми."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"отримати інформацію про поточну програму"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Дозволяє власникові отримувати приватну інформацію про поточну програму на передньому плані екрана."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"відстежувати та контролювати запуски всіх програм"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Дозволяє програмі відстежувати та контролювати, як саме система запускає дії. Шкідливі програми можуть отримати повний контроль над системою. Цей дозвіл потрібний лише для розробки, а не для звичайного користування."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"надсил. запис про видал. пакета"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Дозволяє програмі зчитувати дані про поточний низький рівень споживання заряду акумулятора. Програма може отримувати докладну інформацію про те, якими програмами ви користуєтеся."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"змінювати статистику акумулятора"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Дозволяє програмі змінювати зібрану статистику акумулятора. Не для використання звичайними програмами."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"отримувати статистику роботи програми"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Дозволяє програмі отримувати зібрану статистику роботи програми. Не використовується звичайними програмами."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"змінювати статистику роботи програми"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Дозволяє програмі змінювати зібрану статистику роботи програми. Не використовується звичайними програмами."</string> <string name="permlab_backup" msgid="470013022865453920">"контр. резерв. копіюв. і відн. сист."</string> <string name="permdesc_backup" msgid="6912230525140589891">"Дозволяє програмі контролювати механізми резервного копіювання та відновлення системи. Не для використання звичайними програмами."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"підтверджувати повну операцію резервного копіювання або відновлення"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня методу введення. Ніколи не застосовується для звичайних програм."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"прив’язуватися до служби доступності"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня служби доступності. Ніколи не застосовується для звичайних програм."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"подавати запит на дослідження дотиком"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Дозволяє подавати запит на інтерактивний режим, у якому надаються голосові підказки для елементів, яких торкається користувач, а інтерфейсом можна користуватися за допомогою жестів."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"подавати запит на покращення веб-доступності"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Дозволяє подавати запит на ввімкнення покращень веб-доступності. Наприклад, установлювати сценарії, які робитимуть вміст програм доступнішим."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"прив’язати до текстової служби"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня текстової служби (напр. SpellCheckerService). Ніколи не застосовується для звичайних програм."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"прив’язуватися до служби VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Дозволяє програмі керувати політикою мережі та визначити спеціальні правила для програм."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"змінювати облік використання мережі"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дозволяє програмі змінювати метод підрахунку того, як програми використовують мережу. Не для використання звичайними програмами."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"отримувати доступ до сповіщень"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозволяє програмі отримувати, перевіряти й очищати сповіщення, зокрема опубліковані іншими програмами."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Устан. правила пароля"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролювати довжину паролів для розблокування екрана та дозволені в них символи."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Відстежув. спроби розблок. екрана"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Ключ очищено"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Телефон додано"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Малювання ключа закінчено"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Віджет %2$d з %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Додати віджет."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Порожня область"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Область розблокування розгорнуто."</string> @@ -1047,7 +1062,7 @@ <string name="noApplications" msgid="2991814273936504689">"Жодна програма не може виконати цю дію."</string> <string name="aerr_title" msgid="1905800560317137752"></string> <string name="aerr_application" msgid="932628488013092776">"На жаль, програма <xliff:g id="APPLICATION">%1$s</xliff:g> припинила роботу."</string> - <string name="aerr_process" msgid="4507058997035697579">"На жаль, процес <xliff:g id="PROCESS">%1$s</xliff:g> припинився."</string> + <string name="aerr_process" msgid="4507058997035697579">"На жаль, програма <xliff:g id="PROCESS">%1$s</xliff:g> припинила роботу."</string> <string name="anr_title" msgid="4351948481459135709"></string> <string name="anr_activity_application" msgid="1904477189057199066">"Програма <xliff:g id="APPLICATION">%2$s</xliff:g> не відповідає."\n\n"Закрити її?"</string> <string name="anr_activity_process" msgid="5776209883299089767">"Дія <xliff:g id="ACTIVITY">%1$s</xliff:g> не відповідає."\n\n"Закінчити її?"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index a6e5d4b..7edb530 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Dung lượng"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Truy cập bộ nhớ USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Truy cập thẻ SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Tính năng hỗ trợ truy cập"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Tính năng mà công nghệ hỗ trợ có thể yêu cầu."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"vô hiệu hóa hoặc sửa đổi thanh trạng thái"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Cho phép ứng dụng vô hiệu hóa thanh trạng thái hoặc thêm và xóa biểu tượng hệ thống."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"thanh trạng thái"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Cho phép ứng dụng đọc tin nhắn quảng bá mà thiết bị của bạn nhận được. Tin nhắn quảng bá cảnh báo được gửi ở một số địa điểm nhằm cảnh báo cho bạn về các tình huống khẩn cấp. Các ứng dụng độc hại có thể gây ảnh hưởng đến hiệu suất hoặc hoạt động của thiết bị của bạn khi nhận được tin nhắn quảng bá khẩn cấp."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"gửi tin nhắn SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Cho phép ứng dụng gửi tin nhắn SMS. Việc này có thể dẫn đến các khoản phí không mong muốn. Các ứng dụng độc hai có thể khiến bạn tốn tiền bằng cách gửi tin nhắn mà không cần sự xác nhận của bạn."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"gửi tin nhắn SMS mà không cần xác nhận"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Cho phép ứng dụng gửi tin nhắn SMS. Việc này có thể dẫn đến các khoản phí không mong muốn. Các ứng dụng độc hai có thể khiến bạn tốn tiền bằng cách gửi tin nhắn mà không cần sự xác nhận của bạn."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"gửi sự kiện trả lời qua tin nhắn"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Cho phép ứng dụng gửi yêu cầu đến ứng dụng nhắn tin khác để xử lý các sự kiện trả lời qua tin nhắn cho các cuộc gọi đến."</string> <string name="permlab_readSms" msgid="8745086572213270480">"đọc tin nhắn văn bản của bạn (SMS hoặc MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Cho phép ứng dụng đọc tin nhắn SMS được lưu trữ trên máy tính bảng hoặc thẻ SIM của bạn. Việc này cho phép ứng dụng đọc tất cả tin nhắn SMS, bất kể nội dung hay tính bí mật là gì."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Cho phép ứng dụng đọc tin nhắn SMS được lưu trữ trên điện thoại hoặc thẻ SIM của bạn. Việc này cho phép ứng dụng đọc tất cả tin nhắn SMS, bất kể nội dung hay tính bí mật là gì."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Đặt trình quản lý hoạt động sang trạng thái tắt. Không thực hiện tắt hoàn toàn."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ngăn chuyển đổi ứng dụng"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Ngăn người dùng chuyển sang ứng dụng khác."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"truy cập thông tin ứng dụng hiện tại"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Cho phép chủ sở hữu truy xuất thông tin cá nhân về ứng dụng hiện tại ở nền trước của màn hình."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"giám sát và kiểm soát tất cả hoạt động khởi chạy ứng dụng"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Cho phép ứng dụng giám sát và kiểm soát cách hệ thống khởi chạy các hoạt động. Ứng dụng độc hại hoàn toàn có thể làm tổn hại hệ thống. Quyền này chỉ cần cho mục đích phát triển, không dành cho mục đích sử dụng thông thường."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"gửi truyền phát đã xóa của gói"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Cho phép ứng dụng đọc dữ liệu sử dụng pin mức thấp hiện tại. Có thể cho phép ứng dụng biết thông tin chi tiết về ứng dụng bạn sử dụng."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"sửa đổi số liệu thống kê về pin"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Cho phép ứng dụng sửa đổi các số liệu thống kê về pin đã được thu thập. Không dành cho các ứng dụng thông thường."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"truy xuất số liệu thống kê hoạt động của ứng dụng"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Cho phép ứng dụng truy xuất số liệu thống kê hoạt động của ứng dụng đã thu thập. Không dành cho ứng dụng thông thường."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"sửa đổi số liệu thống kê hoạt động của ứng dụng"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Cho phép ứng dụng sửa đổi số liệu thống kê hoạt động của ứng dụng đã thu thập. Không dành cho ứng dụng thông thường."</string> <string name="permlab_backup" msgid="470013022865453920">"kiểm soát sao lưu và khôi phục hệ thống"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Cho phép ứng dụng kiểm soát cơ chế sao lưu và khôi phục của hệ thống. Không dành cho các ứng dụng thông thường."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"xác nhận bản sao lưu đầy đủ hoặc khôi phục hoạt động"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của phương thức nhập. Không cần thiết cho các ứng dụng thông thường."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"liên kết với dịch vụ truy cập"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ truy cập. Không cần thiết cho các ứng dụng thông thường."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"yêu cầu khám phá bằng cách chạm"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Cho phép chủ sở hữu yêu cầu chế độ tương tác mà các mục đã chạm được đọc to và có thể khám phá giao diện người dùng qua các thao tác."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"yêu cầu khả năng hỗ trợ truy cập web nâng cao"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Cho phép chủ sở hữu yêu cầu bật tính năng cải thiện hỗ trợ truy cập web. Ví dụ: cài đặt tập lệnh để nội dung ứng dụng dễ truy cập hơn."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"liên kết với dịch vụ văn bản"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ văn bản (ví dụ: SpellCheckerService). Không cần thiết cho các ứng dụng thông thường."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"liên kết với dịch vụ VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Cho phép ứng dụng quản lý chính sách mạng và xác định quy tắc dành riêng cho ứng dụng."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"sửa đổi hạch toán sử dụng mạng"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Cho phép ứng dụng sửa đổi cách tính mức sử dụng mạng so với ứng dụng. Không dành cho các ứng dụng thông thường."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"truy cập thông báo"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Cho phép ứng dụng truy xuất, kiểm tra và xóa thông báo, bao gồm những thông báo được đăng bởi các ứng dụng khác."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Đặt quy tắc mật khẩu"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kiểm soát độ dài và ký tự được phép trong mật khẩu mở khóa màn hình."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Giám sát những lần thử mở khóa màn hình"</string> @@ -762,7 +776,7 @@ <string name="lockscreen_password_wrong" msgid="5737815393253165301">"Thử lại"</string> <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Đã vượt quá số lần Mở khóa bằng khuôn mặt tối đa"</string> <string name="lockscreen_plugged_in" msgid="8057762828355572315">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> - <string name="lockscreen_charged" msgid="321635745684060624">"Bị tính phí"</string> + <string name="lockscreen_charged" msgid="321635745684060624">"Pin đầy"</string> <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> <string name="lockscreen_low_battery" msgid="1482873981919249740">"Kết nối bộ sạc của bạn."</string> <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Không có thẻ SIM nào"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Đã xóa hình"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Đã thêm ô"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Đã vẽ xong hình"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Tiện ích %2$d trong số %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Thêm tiện ích."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Trống"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Đã mở rộng vùng khóa."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 425c497..1406064 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"存储"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"访问 USB 存储设备。"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"访问 SD 卡。"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"辅助功能"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"辅助技术可请求启用的功能。"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改状态栏"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用停用状态栏或者增删系统图标。"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"状态栏"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"允许应用读取您的设备收到的小区广播消息。小区广播消息是在某些地区发送的、用于发布紧急情况警告的提醒信息。恶意应用可能会在您收到小区紧急广播时干扰您设备的性能或操作。"</string> <string name="permlab_sendSms" msgid="5600830612147671529">"发送短信"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"允许该应用发送短信。此权限可能会导致意外收费。恶意应用可能会未经您的确认而发送短信,由此产生相关费用。"</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"不经确认直接发送短信"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"允许该应用发送短信。此权限可能会导致意外收费。恶意应用可能会未经您的确认而发送短信,由此产生相关费用。"</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"发送“通过信息回复”事件"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"允许应用向其他信息应用发送请求,以便处理来电的“通过信息回复”事件。"</string> <string name="permlab_readSms" msgid="8745086572213270480">"读取您的文字讯息(短信或彩信)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"允许该应用读取您平板电脑或 SIM 卡上存储的短信。此权限可让该应用读取所有短信,而不考虑短信内容或机密性。"</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"允许该应用读取您手机或 SIM 卡上存储的短信。此权限可让该应用读取所有短信,而不考虑短信内容或机密性。"</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"使活动管理器进入关闭状态。不执行彻底关机。"</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"禁止切换应用"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"阻止用户切换到其他应用。"</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"获取当前应用的信息"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"允许应用针对目前在屏幕前台运行的应用检索相关隐私信息。"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"监控所有应用的启动"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"允许应用监视和控制系统是如何启动活动的。恶意应用可能会完全破坏系统。此权限只有在进行开发时才需要,正常使用情况下绝不需要。"</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"发送包删除的广播"</string> @@ -315,8 +319,12 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"允许应用读取当前电量使用情况的基础数据,此权限可让应用了解关于您使用了哪些应用的详细信息。"</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"修改电池使用统计信息"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"允许该应用修改收集到的电池统计信息。普通应用不应使用此权限。"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"检索应用操作统计信息"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"允许该应用检索收集到的应用操作统计信息。普通应用不应使用此权限。"</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"修改应用操作统计信息"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"允许该应用修改收集到的应用操作统计信息。普通应用不应使用此权限。"</string> <string name="permlab_backup" msgid="470013022865453920">"控制系统备份和还原"</string> - <string name="permdesc_backup" msgid="6912230525140589891">"允许应用控制系统的备份和还原机制。普通应用不能使用此权限。"</string> + <string name="permdesc_backup" msgid="6912230525140589891">"允许应用控制系统的备份和还原机制。普通应用不应使用此权限。"</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"确认完整备份或恢复操作"</string> <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"允许应用启动完整备份确认用户界面。不用于任何应用。"</string> <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"显示未授权的窗口"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允许用户绑定至输入法的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"绑定至辅助服务"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"允许应用绑定至辅助服务的顶级接口。普通应用绝不需要此权限。"</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"请求启用触摸浏览"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"允许应用请求启用互动模式,在该模式下,设备可大声读出用户触摸的内容,而且用户可以通过手势浏览用户界面。"</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"请求启用网页辅助增强功能"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"允许应用请求打开网页辅助增强功能。例如,安装脚本,以方便用户更轻松地访问应用内容。"</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"绑定至文字服务"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"允许用户绑定至文字服务(如 SpellCheckerService)的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"绑定到 VPN 服务"</string> @@ -391,9 +403,9 @@ <string name="permlab_writeSettings" msgid="2226195290955224730">"修改系统设置"</string> <string name="permdesc_writeSettings" msgid="7775723441558907181">"允许应用修改系统的设置数据。恶意应用可能会破坏您的系统配置。"</string> <string name="permlab_writeSecureSettings" msgid="204676251876718288">"修改安全系统设置"</string> - <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"允许应用修改系统的安全设置数据。普通应用不能使用此权限。"</string> + <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"允许应用修改系统的安全设置数据。普通应用不应使用此权限。"</string> <string name="permlab_writeGservices" msgid="2149426664226152185">"修改 Google 服务地图"</string> - <string name="permdesc_writeGservices" msgid="1287309437638380229">"允许应用修改 Google 服务地图。普通应用不能使用此权限。"</string> + <string name="permdesc_writeGservices" msgid="1287309437638380229">"允许应用修改 Google 服务地图。普通应用不应使用此权限。"</string> <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"开机启动"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"允许应用在系统完成引导后立即自动启动。这样可能会延长平板电脑的启动时间,并允许应用始终运行,从而导致平板电脑总体运行速度减慢。"</string> <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"允许应用在系统完成引导后立即自动启动。这样可能会延长手机的启动时间,并允许应用始终运行,从而导致手机总体运行速度减慢。"</string> @@ -492,9 +504,9 @@ <string name="permlab_performCdmaProvisioning" product="default" msgid="5604848095315421425">"直接启动 CDMA 电话设置"</string> <string name="permdesc_performCdmaProvisioning" msgid="1994193538802314186">"允许应用启动 CDMA 配置。恶意应用可能会无端启动 CDMA 配置。"</string> <string name="permlab_locationUpdates" msgid="7785408253364335740">"控制位置更新通知"</string> - <string name="permdesc_locationUpdates" msgid="1120741557891438876">"允许应用启用/停用来自无线装置的位置更新通知。普通应用不能使用此权限。"</string> + <string name="permdesc_locationUpdates" msgid="1120741557891438876">"允许应用启用/停用来自无线装置的位置更新通知。普通应用不应使用此权限。"</string> <string name="permlab_checkinProperties" msgid="7855259461268734914">"访问检入属性"</string> - <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允许应用对登记服务上传的属性拥有读取/写入权限。普通应用不能使用此权限。"</string> + <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允许应用对登记服务上传的属性拥有读取/写入权限。普通应用不应使用此权限。"</string> <string name="permlab_bindGadget" msgid="776905339015863471">"选择小部件"</string> <string name="permdesc_bindGadget" msgid="8261326938599049290">"允许应用告知系统哪些小部件可供哪个应用使用。拥有此权限的应用可向其他应用授予对个人资料的访问权限。普通应用不应使用此权限。"</string> <string name="permlab_modifyPhoneState" msgid="8423923777659292228">"修改手机状态"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允许应用管理网络政策和定义专门针对应用的规则。"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"修改网络使用情况记录方式"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允许该应用修改对于各应用的网络使用情况的统计方式。普通应用不应使用此权限。"</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"查看通知"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"允许该应用检索、检查并清除通知,包括其他应用发布的通知。"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"设置密码规则"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"控制屏幕解锁密码所允许的长度和字符。"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"监视屏幕解锁尝试次数"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"图案已清除"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"已添加单元格"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"图案绘制完成"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s。%3$d的小部件%2$d。"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"添加小部件。"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"空白"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"已展开解锁区域。"</string> @@ -1219,9 +1234,9 @@ <string name="ext_media_nomedia_notification_message" product="default" msgid="3870120652983659641">"SD 卡已移除。请插入新的 SD 卡。"</string> <string name="activity_list_empty" msgid="1675388330786841066">"未找到匹配的活动。"</string> <string name="permlab_pkgUsageStats" msgid="8787352074326748892">"更新组件使用情况统计"</string> - <string name="permdesc_pkgUsageStats" msgid="1106612424254277630">"允许应用修改收集到的组件使用情况统计信息。普通应用不能使用此权限。"</string> + <string name="permdesc_pkgUsageStats" msgid="1106612424254277630">"允许应用修改收集到的组件使用情况统计信息。普通应用不应使用此权限。"</string> <string name="permlab_copyProtectedData" msgid="4341036311211406692">"复制内容"</string> - <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允许应用调用默认的容器服务,以便复制内容。普通应用不能使用此权限。"</string> + <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允许应用调用默认的容器服务,以便复制内容。普通应用不应使用此权限。"</string> <string name="permlab_route_media_output" msgid="1642024455750414694">"更改媒体输出线路"</string> <string name="permdesc_route_media_output" msgid="4932818749547244346">"允许该应用将媒体输出线路更改到其他外部设备。"</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"触摸两次可进行缩放控制"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 2ae304a..b313268 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"儲存"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"存取 USB 儲存裝置。"</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"存取 SD 卡。"</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"協助工具功能"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"輔助技術可要求的功能。"</string> <string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"允許應用程式讀取您裝置收到的區域廣播訊息。某些地點會發出區域廣播警示,警告您有緊急狀況發生。請注意,惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string> <string name="permlab_sendSms" msgid="5600830612147671529">"傳送 SMS 簡訊"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"允許應用程式傳送簡訊,但可能產生非預期的費用。惡意應用程式可能利用此功能擅自傳送簡訊,增加您不必要的額外支出。"</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"不需經過確認即傳送 SMS 簡訊"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"允許應用程式傳送簡訊,但可能產生非預期的費用。惡意應用程式可能利用此功能擅自傳送簡訊,增加您不必要的額外支出。"</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"傳送「透過訊息回應」事件"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"允許應用程式傳送要求給其他簡訊應用程式,以處理來電的「透過訊息回應」事件。"</string> <string name="permlab_readSms" msgid="8745086572213270480">"讀取您的簡訊 (SMS 或 MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"允許應用程式讀取平板電腦或 SIM 卡上儲存的簡訊。這項權限可讓應用程式讀取所有簡訊,包括各種內容及機密簡訊。"</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"允許應用程式讀取手機或 SIM 卡上儲存的簡訊。這項權限可讓應用程式讀取所有簡訊,包括各種內容及機密簡訊。"</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"讓活動管理員進入關機狀態,而不執行完整的關機程序。"</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"防止切換應用程式"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"防止使用者切換到其他應用程式。"</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"取得目前的應用程式資訊"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"允許應用程式針對目前在螢幕前景運作的應用程式擷取私人資訊。"</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"監視及控制所有應用程式的啟動程序"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"允許應用程式監視和控制系統啟動活動的方式。請注意,惡意應用程式可能利用此功能破壞整個系統。這個權限只有開發人員才需要,一般使用者不需使用這個權限。"</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"傳送程式已移除廣播"</string> @@ -315,13 +319,17 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"允許應用程式讀取目前的低電量使用資料。應用程式可能藉此找到一些詳細資訊,例如您所使用的應用程式為何。"</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"修改電池使用統計資料"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"允許應用程式修改收集到的電池使用統計資料 (不建議一般應用程式使用)。"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"擷取應用程式作業統計資料"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"允許應用程式擷取收集到的應用程式作業統計資料 (不建議一般應用程式使用)。"</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"修改應用程式操作統計資料"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"允許應用程式修改收集到的應用程式操作統計資料 (不建議一般應用程式使用)。"</string> <string name="permlab_backup" msgid="470013022865453920">"控制系統備份與還原"</string> <string name="permdesc_backup" msgid="6912230525140589891">"允許應用程式控制系統備份與還原機制 (不建議一般應用程式使用)。"</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"確認完整備份或還原作業"</string> <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"允許應用程式啟動完整備份確認使用者介面 (不建議任何應用程式使用)。"</string> <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"顯示未授權視窗"</string> <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"允許應用程式為內部系統使用者介面建立視窗 (不建議一般應用程式使用)。"</string> - <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"描繪其他應用程式"</string> + <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"在其他應用程式之上顯示内容"</string> <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"允許應用程式在其他應用程式頂層或使用者介面的特定部分繪圖。這可能會干擾您在所有應用程式中的介面使用行為,或是使您在其他應用程式中預期看到的內容發生變化。"</string> <string name="permlab_setAnimationScale" msgid="2805103241153907174">"編輯全域動畫速度"</string> <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"允許應用程式隨時變更全域的動畫速度 (更快或更慢)。"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允許應用程式繫結至輸入法的頂層介面 (一般應用程式不需使用)。"</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"繫結至協助工具服務"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"允許應用程式繫結至協助工具服務的頂層介面 (一般應用程式不需使用)。"</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"要求輕觸探索"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"允許應用程式要求啟用互動模式,讓裝置讀出使用者輕觸的項目,並且讓使用者透過手勢探索使用者介面。"</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"要求增強式網頁協助工具"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"允許應用程式要求啟用網頁協助工具增強功能。例如安裝指令碼,讓使用者更容易存取應用程式的內容。"</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"繫結至文字服務"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"允許應用程式繫結至文字服務 (例如 SpellCheckerService) 的頂層介面 (一般應用程式不需使用)。"</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"繫結至 VPN 服務"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允許應用程式管理網路政策並定義應用程式專用規則。"</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"修改網路使用量計算方式"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允許應用程式修改應用程式網路使用量的計算方式 (不建議一般應用程式使用)。"</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"存取通知"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"允許應用程式擷取、檢查及清除通知 (包括由其他應用程式發佈的通知)。"</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"設定密碼規則"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"控制螢幕解鎖密碼所允許的長度和字元。"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"監視螢幕解鎖嘗試次數"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"已清除解鎖圖形"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"已加入 1 格"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"已畫出解鎖圖形"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s。第 %2$d 個小工具,共 %3$d 個。"</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"新增小工具。"</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"空白"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"解鎖區域已展開。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 43ec792..30893c6 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -225,6 +225,8 @@ <string name="permgrouplab_storage" msgid="1971118770546336966">"Isitoreji"</string> <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"Finyelela kwisitoreji se-USB."</string> <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Finyelela ikhadi le-SD."</string> + <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Izici zokufinyelela"</string> + <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Izici ezingacelwa ubuchwepheshe bokusiza."</string> <string name="permlab_statusBar" msgid="7417192629601890791">"khubaza noma guqula ibha yomumo"</string> <string name="permdesc_statusBar" msgid="8434669549504290975">"Ivumela insiza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"umudwa ochaza ngesimo"</string> @@ -243,8 +245,8 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ivumela uhlelo lokusebenza ukufunda imilayezo yokusakaza yeselula etholwe idivayisi yakho. Izaziso zokusakaza zeselula zilethwa kwezinye izindawo ukukuxwayisa ngezimo ezisheshayo. Izinhlelo zokusebenza ezingalungile zingaphazamisana nokusebenza noma umsebenzi wedivayisi yakho uma ukusakaza kweselula kwesimo esisheshayo kutholwa."</string> <string name="permlab_sendSms" msgid="5600830612147671529">"thumela imiyalezo ye-SMS"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Ivumela uhlelo lokusebenza ukuthumela imilayezo ye-SMS. Lokhu kungaholela emashajini angallindelekile. Izinhlelo zokusebenza ezingalungile zingakubiza imali ngokuthumela imilayezo ngaphandle kokuqinisekisa kwakho."</string> - <string name="permlab_sendSmsNoConfirmation" msgid="4781483105951730228">"thumela i-SMS engenakuqinisekiswa"</string> - <string name="permdesc_sendSmsNoConfirmation" msgid="402569800862935907">"Ivumela uhlelo lokusebenza ukuthumela imilayezo ye-SMS. Lokhu kungaholela emashajini angallindelekile. Izinhlelo zokusebenza ezingalungile zingakubiza imali ngokuthumela imilayezo ngaphandle kokuqinisekisa kwakho."</string> + <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"thumela imicimbi yokuphendula ngemilayezo"</string> + <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Ivumela uhlelo lokusebenza ukuthumela izicelo kwezinye izinhlelo zokusebenza zemilayezo ukuphatha imicimbi yokuphendula ngemilayezo amakholi angenayo."</string> <string name="permlab_readSms" msgid="8745086572213270480">"funda imilayezo yakho ebhaliwe (i-SMS noma i-MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Ivumela uhlelo lokusebenza ukufunda imilayezo ye-SMS elondolozwe kuthebulethi noma ekhadini lakho le-SIM. Lokhu kuvumela uhlelo lokusebenza ukufunda yonke imilayezo ye-SMS, ngaphandle kokuqukethwe noma ukugcinwa kuyimfihlo."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Ivumela uhlelo lokusebenza ukufunda imilayezo ye-SMS elondolozwe efonini noma ekhadini lakho le-SIM. Lokhu kuvumela uhlelo lokusebenza ukufunda yonke imilayezo ye-SMS, ngaphandle kokuqukethwe noma ukugcinwa kuyimfihlo."</string> @@ -299,6 +301,8 @@ <string name="permdesc_shutdown" msgid="7046500838746291775">"Ibeka imeneja yomsebenzi kwisimo sokuvala shaqa. Ayenzi ukuvala shaqa okuphelele."</string> <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"gwema ukushintsha kohlelo lokusebenza"</string> <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Igwema umsebenzisi ukuthi ashintshele kolunye uhlelo lokusebenza."</string> + <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"thola ulwazi lohlelo lokusebenza lwamanje"</string> + <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Ivumela umphathi ukuthi athole ulwazi oluyimfihlo mayelana nohlelo lokusebenza lwamanje ngaphambili kwesikrini."</string> <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"qapha futhi ulawule ukuqaliswa kwazo zonke izinsiza"</string> <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Ivumela insiza ukuthi ihlole futhi ilawule ukuthi isistimu iziqalisa kanjani izehlakalo. Izinzisa ezinobungozi zingensa isistimu ibe sebungozini. Lemvume idingakalela intuthuku kuphela hhay ukusetshenziswa okwejwayelekile."</string> <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"thumela iphakheji yomsakazo okhishiwe"</string> @@ -315,6 +319,10 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Ivumela uhlelo lokusebenza ukufunda idatha yokusebenza yebhethri leleveli ephansi yamanje. Ingavumela uhlelo lokusebenza ukuthola ulwazi lemininingwane mayelana nokuthi iziphi izinhlelo zokusebenza ozisebenzisayo."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"guqula izibalo zebhetri"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Ivumela uhlelo lokusebenza ukuthi luguqule izibalo zebhethri eziqoqiwe. Akwenzelwe ukuthi kusetshenziswe izinhlelo zokusebenza ezijwayelekile."</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"thola izibalo zokusebenza kohlelo lokusebenza"</string> + <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Ivumela uhlelo lokusebenza ukuthi lithole izibalo zokusebenza kohlelo lokusebenza. Akumele kusetshenziswe izinhlelo zokusebenza ezijwayelekile."</string> + <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"shintsha izinombolo zokusebenza zohlelo lokusebenza"</string> + <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Ivumela uhlelo lokusebenza ukuthi lishintshe izinombolo zokusebenza kohlelo lokusebenza lokuqoqiwe. Akufanele kusetshenziswe izinhlelo zokusebenza ezijwayelekile."</string> <string name="permlab_backup" msgid="470013022865453920">"lawula ukusekela ngokulondoloza uhlelo bese ubuyisela esimweni"</string> <string name="permdesc_backup" msgid="6912230525140589891">"Ivumela insiza ukuthi ilawule isipele sesistimu kanye nohlelo lokubuyiselwa kabusha. Ayenzelwe ukusetshenziswa izinsiza ezijwayelekile."</string> <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"qinisekisa isipele sonke noma buyisela futhi ukusebenza"</string> @@ -338,6 +346,10 @@ <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Ivumela isimeli ukuhlanganisa uxhumano nomsebenzisi wezinga eliphezulu lendlela yokufaka. Ayisoze yadingeka kwizinhlelo ezivamile."</string> <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"hlanganisa kusevisi yokufinyeleleka"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Ivumela isibambi ukuhlanganisa uxhumo nomsebenzisi kwezinga eliphezulu lesevisi yesinqunjwana. Akusoze kwadingekela izinhlelo zokusebenza ezivamile."</string> + <string name="permlab_canRequestTouchExplorationMode" msgid="6094034289937541846">"cela ukuhlola ngokuthinta"</string> + <string name="permdesc_canRequestTouchExplorationMode" msgid="940314268922270663">"Ivumela i-hoder ukuthi icele imodi yokusebenzisana lapho izinto ezithintiwe zikhulunywa ngokumemezwa ne-UI ingahlolwa ngezimpawu."</string> + <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"cela ukufinyelela kuwebhu okuthuthukisiwe"</string> + <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Ivumela i-hoder ukucela ukunika amandla ukuthuthukiswa kokufinyelela iwebhu. isibonelo, ukufaka izikripthi kusuka ku-Google ukwenza okuqukethwe kohlelo lokusebenza ukuthi kufinyeleleke."</string> <string name="permlab_bindTextService" msgid="7358378401915287938">"bophezela kunsizakalo yombhalo"</string> <string name="permdesc_bindTextService" msgid="8151968910973998670">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lwesixhumi esibonakalayo sensizakalo yombhalo(isb. InsizakaloYokuhlolaUkubhala). Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string> <string name="permlab_bindVpnService" msgid="4708596021161473255">"hlanganisa kwinsizakalo ye-VPN"</string> @@ -605,6 +617,8 @@ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Ivumela insiza ukuthi yengamele iigomo iphinde ichaze imithetho ehambisana ngqo nensiza."</string> <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"lungisa ukubala kokusebenza kohleloxhumano"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ivumela insiza ukuthi iguqule ukuthii ukusetshenziswa kwenethiwekhi kumiswa kanjani ezinsizeni. Ayisetshenziswa izinsiza ezijwayelekile."</string> + <string name="permlab_accessNotifications" msgid="7673416487873432268">"finyelela kuzaziso"</string> + <string name="permdesc_accessNotifications" msgid="458457742683431387">"Ivumela uhlelo lokusebenza ukuthi lithole, lihlole, liphinde lisuse izaziso, ezifaka lezo ezithunyelwe ezinye izinhlelo zokusebenza."</string> <string name="policylab_limitPassword" msgid="4497420728857585791">"Misa imithetho yephasiwedi"</string> <string name="policydesc_limitPassword" msgid="3252114203919510394">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi okuvula isikrini"</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Gaka imizamo yokuvula isikrini"</string> @@ -810,6 +824,7 @@ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Iphethini isusiwe"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Kwengezwe"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Iphethini isiphelile"</string> + <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. iwijethi %2$d ye-%3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"Engeza iwijethi."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"Akunalutho"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"Indawo yokuvula inwetshisiwe."</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 8e66a77..c73ff81 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -312,6 +312,8 @@ <attr name="windowNoTitle" format="boolean" /> <!-- Flag indicating whether this window should fill the entire screen. --> <attr name="windowFullscreen" format="boolean" /> + <!-- Flag indicating whether this window should extend into overscan region. --> + <attr name="windowOverscan" format="boolean" /> <!-- Flag indicating whether this is a floating window. --> <attr name="windowIsFloating" format="boolean" /> <!-- Flag indicating whether this is a translucent window. --> @@ -1541,6 +1543,9 @@ <enum name="KEYCODE_YEN" value="216" /> <enum name="KEYCODE_RO" value="217" /> <enum name="KEYCODE_KANA" value="218" /> + <enum name="KEYCODE_ASSIST" value="219" /> + <enum name="KEYCODE_BRIGHTNESS_DOWN" value="220" /> + <enum name="KEYCODE_BRIGHTNESS_UP" value="221" /> </attr> <!-- ***************************************************************** --> @@ -1559,6 +1564,7 @@ <attr name="windowFrame" /> <attr name="windowNoTitle" /> <attr name="windowFullscreen" /> + <attr name="windowOverscan" /> <attr name="windowIsFloating" /> <attr name="windowIsTranslucent" /> <attr name="windowShowWallpaper" /> @@ -2246,6 +2252,14 @@ See {@link android.view.ViewGroup#setMotionEventSplittingEnabled(boolean)} for more information. --> <attr name="splitMotionEvents" format="boolean" /> + + <!-- Defines the layout mode of this ViewGroup. --> + <attr name="layoutMode"> + <!-- Use the children's clip bounds when laying out this container. --> + <enum name="clipBounds" value="0" /> + <!-- Use the children's optical bounds when laying out this container. --> + <enum name="opticalBounds" value="1" /> + </attr> </declare-styleable> <!-- A {@link android.view.ViewStub} lets you lazily include other XML layouts @@ -2501,6 +2515,8 @@ <flag name="flagIncludeNotImportantViews" value="0x00000002" /> <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} --> <flag name="flagRequestTouchExplorationMode" value="0x00000004" /> + <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY} --> + <flag name="flagRequestEnhancedWebAccessibility" value="0x00000008" /> </attr> <!-- Component name of an activity that allows the user to modify the settings for this service. This setting cannot be changed at runtime. --> @@ -2951,6 +2967,9 @@ <!-- Timeout between frames of animation in milliseconds {@deprecated Not used by the framework.} --> <attr name="animationResolution" format="integer" /> + <!-- Defines if the associated drawables need to be mirrored when in RTL mode. + Default is false --> + <attr name="mirrorForRtl" format="boolean" /> </declare-styleable> <declare-styleable name="SeekBar"> @@ -3045,6 +3064,14 @@ <attr name="textColorLink" /> <!-- Present the text in ALL CAPS. This may use a small-caps form when available. --> <attr name="textAllCaps" format="boolean" /> + <!-- Place a shadow of the specified color behind the text. --> + <attr name="shadowColor" format="color" /> + <!-- Horizontal offset of the shadow. --> + <attr name="shadowDx" format="float" /> + <!-- Vertical offset of the shadow. --> + <attr name="shadowDy" format="float" /> + <!-- Radius of the shadow. --> + <attr name="shadowRadius" format="float" /> </declare-styleable> <declare-styleable name="TextClock"> <!-- Specifies the formatting pattern used to show the time and/or date @@ -3179,13 +3206,13 @@ specified number. --> <attr name="maxLength" format="integer" min="0" /> <!-- Place a shadow of the specified color behind the text. --> - <attr name="shadowColor" format="color" /> + <attr name="shadowColor" /> <!-- Horizontal offset of the shadow. --> - <attr name="shadowDx" format="float" /> + <attr name="shadowDx" /> <!-- Vertical offset of the shadow. --> - <attr name="shadowDy" format="float" /> + <attr name="shadowDy" /> <!-- Radius of the shadow. --> - <attr name="shadowRadius" format="float" /> + <attr name="shadowRadius" /> <attr name="autoLink" /> <!-- If set to false, keeps the movement method from being set to the link movement method even if autoLink causes links @@ -4022,20 +4049,21 @@ <declare-styleable name="BitmapDrawable"> <!-- Identifier of the bitmap file. This attribute is mandatory. --> <attr name="src" /> - <!-- Enables or disables antialiasing. --> + <!-- Enables or disables antialiasing. Antialiasing can be used to smooth the + edges of a bitmap when rotated. Default value is false. --> <attr name="antialias" format="boolean" /> <!-- Enables or disables bitmap filtering. Filtering is used when the bitmap is - shrunk or stretched to smooth its apperance. --> + shrunk or stretched to smooth its apperance. Default value is true. --> <attr name="filter" format="boolean" /> <!-- Enables or disables dithering of the bitmap if the bitmap does not have the same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with - an RGB 565 screen). --> + an RGB 565 screen). Default value is true. --> <attr name="dither" /> <!-- Defines the gravity for the bitmap. The gravity indicates where to position the drawable in its container if the bitmap is smaller than the container. --> <attr name="gravity" /> <!-- Defines the tile mode. When the tile mode is enabled, the bitmap is repeated. - Gravity is ignored when the tile mode is enabled. --> + Gravity is ignored when the tile mode is enabled. Default value is "disabled". --> <attr name="tileMode"> <!-- Do not tile the bitmap. This is the default value. --> <enum name="disabled" value="-1" /> @@ -4047,6 +4075,10 @@ mirror images so that adjacent images always seam. --> <enum name="mirror" value="2" /> </attr> + <!-- Enables or disables the mipmap hint. See + {@link android.graphics.Bitmap#setHasMipMap(boolean)} for more information. + Default value is false. --> + <attr name="mipMap" format="boolean" /> </declare-styleable> <!-- Drawable used to draw 9-patches. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 354a0f4..ccdddd8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -535,12 +535,11 @@ <!-- Control the behavior when the user long presses the home button. 0 - Nothing - 1 - Recent apps dialog - 2 - Recent apps view in SystemUI + 1 - Recent apps view in SystemUI This needs to match the constants in policy/src/com/android/internal/policy/impl/PhoneWindowManager.java --> - <integer name="config_longPressOnHomeBehavior">2</integer> + <integer name="config_longPressOnHomeBehavior">1</integer> <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support. The N entries of this array define N + 1 control points as follows: @@ -880,6 +879,16 @@ <!-- Remote server that can provide NTP responses. --> <string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string> + <!-- Normal polling frequency in milliseconds --> + <integer name="config_ntpPollingInterval">864000000</integer> + <!-- Try-again polling interval in milliseconds, in case the network request failed --> + <integer name="config_ntpPollingIntervalShorter">60000</integer> + <!-- Number of times to try again with the shorter interval, before backing + off until the normal polling interval. A value < 0 indicates infinite. --> + <integer name="config_ntpRetry">3</integer> + <!-- If the time difference is greater than this threshold in milliseconds, + then update the time. --> + <integer name="config_ntpThreshold">5000</integer> <!-- Timeout to wait for NTP server response. --> <integer name="config_ntpTimeout">20000</integer> diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml index bdcab39..b49e7bd 100644 --- a/core/res/res/values/donottranslate.xml +++ b/core/res/res/values/donottranslate.xml @@ -26,4 +26,8 @@ <string name="lock_pattern_view_aspect">square</string> <!-- @hide DO NOT TRANSLATE. Separator between the hour and minute elements in a TimePicker widget --> <string name="time_picker_separator">:</string> + <!-- @hide DO NOT TRANSLATE. ICU pattern for "Mon, 14 January" --> + <string name="icu_abbrev_wday_month_day_no_year">eeeMMMMd</string> + <!-- @hide DO NOT TRANSLATE. date formatting pattern for system ui.--> + <string name="system_ui_date_pattern">@string/icu_abbrev_wday_month_day_no_year</string> </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 547a192..21bae04 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -79,4 +79,5 @@ <item type="id" name="action_menu_presenter" /> <item type="id" name="overflow_menu_presenter" /> <item type="id" name="popup_submenu_presenter" /> + <item type="id" name="action_bar_spinner" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a5dae7e..42e5cf1 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2028,4 +2028,26 @@ <public type="style" name="Widget.DeviceDefault.CheckedTextView" id="0x010301db" /> <public type="style" name="Widget.DeviceDefault.Light.CheckedTextView" id="0x010301dc" /> +<!-- =============================================================== + Resources added in version 18 of the platform + =============================================================== --> + <eat-comment /> + + <public type="attr" name="mipMap" id="0x010103cd" /> + <public type="attr" name="mirrorForRtl" id="0x010103ce" /> + + <!-- =============================================================== + Resources added in version 19 of the platform + =============================================================== --> + <eat-comment /> + + <public type="attr" name="windowOverscan" /> + <public type="style" name="Theme.NoTitleBar.Overscan" /> + <public type="style" name="Theme.Light.NoTitleBar.Overscan" /> + <public type="style" name="Theme.Black.NoTitleBar.Overscan" /> + <public type="style" name="Theme.Holo.NoActionBar.Overscan" /> + <public type="style" name="Theme.Holo.Light.NoActionBar.Overscan" /> + <public type="style" name="Theme.DeviceDefault.NoActionBar.Overscan" /> + <public type="style" name="Theme.DeviceDefault.Light.NoActionBar.Overscan" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index cb8d0e5..5fc26fc 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -535,6 +535,11 @@ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgroupdesc_storage" product="default">Access the SD card.</string> + <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permgrouplab_accessibilityFeatures">Accessibility features</string> + <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permgroupdesc_accessibilityFeatures">Features that assistive technology can request.</string> + <!-- Permissions --> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -596,12 +601,12 @@ This may result in unexpected charges. Malicious apps may cost you money by sending messages without your confirmation.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_sendSmsNoConfirmation">send SMS messages with no confirmation</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_sendSmsNoConfirmation">Allows the app to send SMS - messages. This may result in unexpected charges. Malicious apps may cost - you money by sending messages without your confirmation.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permlab_sendRespondViaMessageRequest">send respond-via-message events</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permdesc_sendRespondViaMessageRequest">Allows the app to send + requests to other messaging apps to handle respond-via-message events for incoming + calls.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readSms">read your text messages (SMS or MMS)</string> @@ -784,6 +789,12 @@ another app.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_getTopActivityInfo">get current app info</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_getTopActivityInfo">Allows the holder to retrieve private information + about the current application in the foreground of the screen.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_runSetActivityWatcher">monitor and control all app launching</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_runSetActivityWatcher">Allows the app to @@ -842,6 +853,18 @@ <string name="permdesc_updateBatteryStats">Allows the app to modify collected battery statistics. Not for use by normal apps.</string> + <!-- [CHAR LIMIT=NONE] Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_getAppOpsStats">retrieve app ops statistics</string> + <!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_getAppOpsStats">Allows the app to retrieve + collected application operation statistics. Not for use by normal apps.</string> + + <!-- [CHAR LIMIT=NONE] Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_updateAppOpsStats">modify app ops statistics</string> + <!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_updateAppOpsStats">Allows the app to modify + collected application operation statistics. Not for use by normal apps.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_backup">control system backup and restore</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -917,6 +940,20 @@ interface of an accessibility service. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_canRequestTouchExplorationMode">request explore by touch</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_canRequestTouchExplorationMode">Allows the hoder to request an + interaction mode in which touched items are spoken aloud and the UI can be explored + via gestures.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_canRequestEnahncedWebAccessibility">request enhanced web accessibility</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_canRequestEnahncedWebAccessibility">Allows the hoder to request + enabling of web accessibility enhancements. For example, installing scripts to make + app content more accessible.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindTextService">bind to a text service</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bindTextService">Allows the holder to bind to the top-level @@ -1769,6 +1806,11 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_modifyNetworkAccounting">Allows the app to modify how network usage is accounted against apps. Not for use by normal apps.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_accessNotifications">access notifications</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</string> + <!-- Policy administration --> <!-- Title of policy access to limiting the user's password choices --> @@ -2685,16 +2727,16 @@ service to put the device into explore by touch mode, displayed as a dialog message when the user selects to enables the service. (tablet). [CHAR LIMIT=NONE] --> <string name="enable_explore_by_touch_warning_message" product="tablet"> - <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch. - When Explore by Touch is turned on, you can hear or see descriptions of what\'s under - your finger or perform gestures to interact with the tablet.</string> + <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch. + When Explore by Touch is turned on, you can hear or see descriptions of what\'s under + your finger or perform gestures to interact with the tablet.</string> <!-- Summary for a warning message about the interaction model changes after allowing an accessibility service to put the device into explore by touch mode, displayed as a dialog message when the user selects to enables the service. (default). [CHAR LIMIT=NONE] --> <string name="enable_explore_by_touch_warning_message" product="default"> - <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch. - When Explore by Touch is turned on, you can hear or see descriptions of what\'s under - your finger or perform gestures to interact with the phone.</string> + <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch. + When Explore by Touch is turned on, you can hear or see descriptions of what\'s under + your finger or perform gestures to interact with the phone.</string> <!-- String used to display the date. This is the string to say something happened 1 month ago. --> <string name="oneMonthDurationPast">1 month ago</string> @@ -3470,8 +3512,8 @@ <string name="vpn_lockdown_connected">Always-on VPN connected</string> <!-- Notification title when error connecting to lockdown VPN. --> <string name="vpn_lockdown_error">Always-on VPN error</string> - <!-- Notification body that indicates user can touch to cycle lockdown VPN connection. --> - <string name="vpn_lockdown_reset">Touch to reset connection</string> + <!-- Notification body that indicates user can touch to configure lockdown VPN connection. --> + <string name="vpn_lockdown_config">Touch to configure</string> <!-- Localized strings for WebView --> <!-- Label for button in a WebView that will open a chooser to choose a file to upload --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index f489786..56c2235 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -370,6 +370,7 @@ please see styles_device_defaults.xml. <item name="android:maxWidth">48dip</item> <item name="android:minHeight">48dip</item> <item name="android:maxHeight">48dip</item> + <item name="android:mirrorForRtl">false</item> </style> <style name="Widget.ProgressBar.Large"> @@ -410,6 +411,7 @@ please see styles_device_defaults.xml. <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item> <item name="android:minHeight">20dip</item> <item name="android:maxHeight">20dip</item> + <item name="android:mirrorForRtl">true</item> </style> <style name="Widget.SeekBar"> @@ -421,6 +423,7 @@ please see styles_device_defaults.xml. <item name="android:thumb">@android:drawable/seek_thumb</item> <item name="android:thumbOffset">8dip</item> <item name="android:focusable">true</item> + <item name="android:mirrorForRtl">true</item> </style> <style name="Widget.RatingBar"> @@ -430,6 +433,7 @@ please see styles_device_defaults.xml. <item name="android:minHeight">57dip</item> <item name="android:maxHeight">57dip</item> <item name="android:thumb">@null</item> + <item name="android:mirrorForRtl">true</item> </style> <style name="Widget.RatingBar.Indicator"> @@ -1745,6 +1749,7 @@ please see styles_device_defaults.xml. <item name="android:focusable">true</item> <item name="android:paddingStart">16dip</item> <item name="android:paddingEnd">16dip</item> + <item name="android:mirrorForRtl">true</item> </style> <style name="Widget.Holo.RatingBar" parent="Widget.RatingBar"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index caeccb5..42ce336 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -172,8 +172,6 @@ <java-symbol type="id" name="text" /> <java-symbol type="id" name="time" /> <java-symbol type="id" name="time_current" /> - <java-symbol type="id" name="timeDisplayBackground" /> - <java-symbol type="id" name="timeDisplayForeground" /> <java-symbol type="id" name="titleDivider" /> <java-symbol type="id" name="titleDividerTop" /> <java-symbol type="id" name="timePicker" /> @@ -218,6 +216,7 @@ <java-symbol type="id" name="sms_short_code_remember_choice_checkbox" /> <java-symbol type="id" name="sms_short_code_remember_undo_instruction" /> <java-symbol type="id" name="breadcrumb_section" /> + <java-symbol type="id" name="action_bar_spinner" /> <java-symbol type="attr" name="actionModeShareDrawable" /> <java-symbol type="attr" name="alertDialogCenterButtons" /> @@ -282,6 +281,10 @@ <java-symbol type="integer" name="config_cursorWindowSize" /> <java-symbol type="integer" name="config_longPressOnPowerBehavior" /> <java-symbol type="integer" name="config_max_pan_devices" /> + <java-symbol type="integer" name="config_ntpPollingInterval" /> + <java-symbol type="integer" name="config_ntpPollingIntervalShorter" /> + <java-symbol type="integer" name="config_ntpRetry" /> + <java-symbol type="integer" name="config_ntpThreshold" /> <java-symbol type="integer" name="config_ntpTimeout" /> <java-symbol type="integer" name="config_wifi_framework_scan_interval" /> <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" /> @@ -1266,27 +1269,20 @@ <java-symbol type="drawable" name="kg_widget_bg_padded" /> <java-symbol type="id" name="action_mode_bar_stub" /> <java-symbol type="id" name="alarm_status" /> - <java-symbol type="id" name="backspace" /> <java-symbol type="id" name="button0" /> <java-symbol type="id" name="button4" /> <java-symbol type="id" name="button5" /> <java-symbol type="id" name="button6" /> <java-symbol type="id" name="button7" /> - <java-symbol type="id" name="carrier" /> <java-symbol type="id" name="date" /> <java-symbol type="id" name="eight" /> - <java-symbol type="id" name="emergencyCallButton" /> <java-symbol type="id" name="face_unlock_area_view" /> <java-symbol type="id" name="face_unlock_cancel_button" /> <java-symbol type="id" name="five" /> - <java-symbol type="id" name="forgotPatternButton" /> <java-symbol type="id" name="four" /> - <java-symbol type="id" name="headerText" /> <java-symbol type="id" name="icon_menu_presenter" /> - <java-symbol type="id" name="instructions" /> <java-symbol type="id" name="keyboard" /> <java-symbol type="id" name="list_menu_presenter" /> - <java-symbol type="id" name="lockPattern" /> <java-symbol type="id" name="lock_screen" /> <java-symbol type="id" name="login" /> <java-symbol type="id" name="nine" /> @@ -1299,24 +1295,14 @@ <java-symbol type="id" name="password" /> <java-symbol type="id" name="passwordEntry" /> <java-symbol type="id" name="pinEntry" /> - <java-symbol type="id" name="pinDel" /> - <java-symbol type="id" name="pinDisplay" /> - <java-symbol type="id" name="owner_info" /> - <java-symbol type="id" name="pukDel" /> - <java-symbol type="id" name="pukDisplay" /> <java-symbol type="id" name="right_icon" /> <java-symbol type="id" name="seven" /> <java-symbol type="id" name="six" /> <java-symbol type="id" name="status" /> - <java-symbol type="id" name="status1" /> <java-symbol type="id" name="switch_ime_button" /> <java-symbol type="id" name="three" /> <java-symbol type="id" name="title_container" /> - <java-symbol type="id" name="topHeader" /> - <java-symbol type="id" name="transport" /> - <java-symbol type="id" name="transport_bg_protect" /> <java-symbol type="id" name="two" /> - <java-symbol type="id" name="unlock_widget" /> <java-symbol type="id" name="zero" /> <java-symbol type="id" name="keyguard_message_area" /> <java-symbol type="id" name="keyguard_click_area" /> @@ -1367,17 +1353,6 @@ <java-symbol type="integer" name="kg_carousel_angle" /> <java-symbol type="layout" name="global_actions_item" /> <java-symbol type="layout" name="global_actions_silent_mode" /> - <java-symbol type="layout" name="keyguard_screen_glogin_unlock" /> - <java-symbol type="layout" name="keyguard_screen_password_landscape" /> - <java-symbol type="layout" name="keyguard_screen_password_portrait" /> - <java-symbol type="layout" name="keyguard_screen_sim_pin_landscape" /> - <java-symbol type="layout" name="keyguard_screen_sim_pin_portrait" /> - <java-symbol type="layout" name="keyguard_screen_sim_puk_landscape" /> - <java-symbol type="layout" name="keyguard_screen_sim_puk_portrait" /> - <java-symbol type="layout" name="keyguard_screen_tab_unlock" /> - <java-symbol type="layout" name="keyguard_screen_tab_unlock_land" /> - <java-symbol type="layout" name="keyguard_screen_unlock_landscape" /> - <java-symbol type="layout" name="keyguard_screen_unlock_portrait" /> <java-symbol type="layout" name="keyguard_selector_view" /> <java-symbol type="layout" name="keyguard_pattern_view" /> <java-symbol type="layout" name="keyguard_password_view" /> @@ -1399,6 +1374,7 @@ <java-symbol type="layout" name="keyguard_transport_control_view" /> <java-symbol type="layout" name="keyguard_status_view" /> <java-symbol type="string" name="abbrev_wday_month_day_no_year" /> + <java-symbol type="string" name="system_ui_date_pattern" /> <java-symbol type="string" name="android_upgrading_title" /> <java-symbol type="string" name="bugreport_title" /> <java-symbol type="string" name="bugreport_message" /> @@ -1540,6 +1516,7 @@ <java-symbol type="color" name="config_defaultNotificationColor" /> <java-symbol type="drawable" name="ic_notification_ime_default" /> <java-symbol type="drawable" name="ic_notify_wifidisplay" /> + <java-symbol type="drawable" name="ic_menu_refresh" /> <java-symbol type="drawable" name="stat_notify_car_mode" /> <java-symbol type="drawable" name="stat_notify_disabled" /> <java-symbol type="drawable" name="stat_notify_disk_full" /> @@ -1673,7 +1650,7 @@ <java-symbol type="string" name="vpn_lockdown_connecting" /> <java-symbol type="string" name="vpn_lockdown_connected" /> <java-symbol type="string" name="vpn_lockdown_error" /> - <java-symbol type="string" name="vpn_lockdown_reset" /> + <java-symbol type="string" name="vpn_lockdown_config" /> <java-symbol type="string" name="wallpaper_binding_label" /> <java-symbol type="string" name="wifi_display_notification_title" /> <java-symbol type="string" name="wifi_display_notification_message" /> @@ -1816,4 +1793,9 @@ <!-- From PinyinIME(!!!) --> <java-symbol type="string" name="inputMethod" /> + + <!-- From Chromium-WebView --> + <java-symbol type="attr" name="actionModeWebSearchDrawable" /> + <java-symbol type="string" name="websearch" /> + </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 75850dd..411419b 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -166,6 +166,7 @@ please see themes_device_defaults.xml. <item name="windowFrame">@null</item> <item name="windowNoTitle">false</item> <item name="windowFullscreen">false</item> + <item name="windowOverscan">false</item> <item name="windowIsFloating">false</item> <item name="windowContentOverlay">@null</item> <item name="windowShowWallpaper">false</item> @@ -403,6 +404,14 @@ please see themes_device_defaults.xml. <item name="android:windowContentOverlay">@null</item> </style> + <!-- Variant of {@link #Theme} that has no title bar and no status bar and extending + into the display overscan region. --> + <style name="Theme.NoTitleBar.Overscan"> + <item name="android:windowFullscreen">true</item> + <item name="android:windowOverscan">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> + <!-- Theme for a light background with dark text on top. Set your activity to this theme if you would like such an appearance. As with the default theme, you should try to assume little more than that the @@ -495,6 +504,14 @@ please see themes_device_defaults.xml. <item name="android:windowContentOverlay">@null</item> </style> + <!-- Variant of {@link #Theme_Light} that has no title bar and + no status bar and extending into the display overscan region. --> + <style name="Theme.Light.NoTitleBar.Overscan"> + <item name="android:windowFullscreen">true</item> + <item name="android:windowOverscan">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> + <!-- Variant on {@link #Theme} that ensures the background is completely black. This is useful for things like image viewers and media players. If you want the normal (dark background) theme @@ -516,6 +533,14 @@ please see themes_device_defaults.xml. <item name="android:windowContentOverlay">@null</item> </style> + <!-- Variant of {@link #Theme_Black} that has no title bar and + no status bar and extending into the display overscan region. --> + <style name="Theme.Black.NoTitleBar.Overscan"> + <item name="android:windowFullscreen">true</item> + <item name="android:windowOverscan">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> + <!-- Theme for windows that want to have the user's selected wallpaper appear behind them (for API level 10 and lower). --> <style name="Theme.Wallpaper"> @@ -1010,6 +1035,7 @@ please see themes_device_defaults.xml. <item name="windowFrame">@null</item> <item name="windowNoTitle">false</item> <item name="windowFullscreen">false</item> + <item name="windowOverscan">false</item> <item name="windowIsFloating">false</item> <item name="windowContentOverlay">@null</item> <item name="windowShowWallpaper">false</item> @@ -1324,6 +1350,7 @@ please see themes_device_defaults.xml. <item name="windowFrame">@null</item> <item name="windowNoTitle">false</item> <item name="windowFullscreen">false</item> + <item name="windowOverscan">false</item> <item name="windowIsFloating">false</item> <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item> <item name="windowShowWallpaper">false</item> @@ -1563,6 +1590,14 @@ please see themes_device_defaults.xml. <item name="android:windowContentOverlay">@null</item> </style> + <!-- Variant of the holographic (dark) theme that has no title bar and fills + the entire screen and extends into the display overscan region. --> + <style name="Theme.Holo.NoActionBar.Overscan"> + <item name="android:windowFullscreen">true</item> + <item name="android:windowOverscan">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> + <!-- Variant of the holographic (light) theme with no action bar. --> <style name="Theme.Holo.Light.NoActionBar"> <item name="android:windowActionBar">false</item> @@ -1576,6 +1611,14 @@ please see themes_device_defaults.xml. <item name="android:windowContentOverlay">@null</item> </style> + <!-- Variant of the holographic (light) theme that has no title bar and fills + the entire screen and extends into the display overscan region. --> + <style name="Theme.Holo.Light.NoActionBar.Overscan"> + <item name="android:windowFullscreen">true</item> + <item name="android:windowOverscan">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> + <!-- Dialog themes for Holo --> <eat-comment /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 4178cc4..71aa5e6 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -202,12 +202,17 @@ easier. <!-- Variant of {@link #Theme_DeviceDefault} with no action bar --> <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Holo.NoActionBar" > - </style> + <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar --> <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Holo.NoActionBar.Fullscreen" > + </style> + <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and + extending in to overscan region. --> + <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Holo.NoActionBar.Overscan" > </style> + <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style --> <style name="Theme.DeviceDefault.Light" parent="Theme.Holo.Light" > <!-- Text styles --> @@ -356,11 +361,14 @@ easier. </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar --> <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Holo.Light.NoActionBar" > - </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar --> <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Holo.Light.NoActionBar.Fullscreen" > - + </style> + <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar + and extending in to overscan region--> + <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" + parent="Theme.Holo.Light.NoActionBar.Overscan" > </style> <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be floating (not fill the entire screen), and puts a frame around its contents. You can set this diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index 5db7ffc..2c34d47 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -17,7 +17,7 @@ <!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.connectivitymanagertest" - android:sharedUserId="com.android.uid.test"> + android:sharedUserId="android.uid.system"> <!-- We add an application tag here just so that we can indicate that this package needs to link against the android.test library, diff --git a/core/tests/benchmarks/src/android/net/TrafficStatsBenchmark.java b/core/tests/benchmarks/src/android/net/TrafficStatsBenchmark.java new file mode 100644 index 0000000..5a29adc --- /dev/null +++ b/core/tests/benchmarks/src/android/net/TrafficStatsBenchmark.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import com.google.caliper.SimpleBenchmark; + +public class TrafficStatsBenchmark extends SimpleBenchmark { + public void timeGetUidRxBytes(int reps) { + for (int i = 0; i < reps; i++) { + TrafficStats.getUidRxBytes(android.os.Process.myUid()); + } + } + + public void timeGetMobileRxBytes(int reps) { + for (int i = 0; i < reps; i++) { + TrafficStats.getMobileRxBytes(); + } + } + + public void timeGetTotalRxBytes(int reps) { + for (int i = 0; i < reps; i++) { + TrafficStats.getTotalRxBytes(); + } + } +} diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java new file mode 100644 index 0000000..2174be5 --- /dev/null +++ b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.net; + +import android.net.NetworkStats; +import android.os.SystemClock; + +import com.google.caliper.SimpleBenchmark; + +import java.io.File; + +public class NetworkStatsFactoryBenchmark extends SimpleBenchmark { + private File mStats; + + // TODO: consider staging stats file with different number of rows + + @Override + protected void setUp() { + mStats = new File("/proc/net/xt_qtaguid/stats"); + } + + @Override + protected void tearDown() { + mStats = null; + } + + public void timeReadNetworkStatsDetailJava(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + NetworkStatsFactory.javaReadNetworkStatsDetail(mStats, NetworkStats.UID_ALL); + } + } + + public void timeReadNetworkStatsDetailNative(int reps) { + for (int i = 0; i < reps; i++) { + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); + NetworkStatsFactory.nativeReadNetworkStatsDetail( + stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL); + } + } +} diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 41f8536..f8b26bc 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1241,13 +1241,6 @@ </intent-filter> </activity> - <activity android:name="android.accessibilityservice.InterrogationActivity"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> - </intent-filter> - </activity> - <activity android:name="android.animation.BasicAnimatorActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java deleted file mode 100644 index a9f144b..0000000 --- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accessibilityservice; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; - -import com.android.frameworks.coretests.R; - -/** - * Activity for testing the accessibility APIs for "interrogation" of - * the screen content. These APIs allow exploring the screen and - * requesting an action to be performed on a given view from an - * AccessiiblityService. - */ -public class InterrogationActivity extends Activity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.interrogation_activity); - - findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - /* do nothing */ - } - }); - findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() { - public boolean onLongClick(View v) { - return true; - } - }); - } -} diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java deleted file mode 100644 index 3dc140b..0000000 --- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java +++ /dev/null @@ -1,472 +0,0 @@ -/** - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accessibilityservice; - -import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS; -import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS; -import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT; -import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION; - -import android.graphics.Rect; -import android.os.SystemClock; -import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; - -import com.android.frameworks.coretests.R; -import com.android.internal.util.Predicate; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -/** - * Activity for testing the accessibility APIs for "interrogation" of - * the screen content. These APIs allow exploring the screen and - * requesting an action to be performed on a given view from an - * AccessiiblityService. - */ -public class InterrogationActivityTest - extends ActivityInstrumentationTestCase2<InterrogationActivity> { - private static final boolean DEBUG = false; - - private static String LOG_TAG = "InterrogationActivityTest"; - - // Timeout for the accessibility state of an Activity to be fully initialized. - private static final int TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS = 5000; - - // Timeout for which non getting accessibility events considers the app idle. - private static final long IDLE_EVENT_TIME_DELTA_MILLIS = 200; - - // Timeout in which to wait for idle device. - private static final long GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS = 1000; - - // Handle to a connection to the AccessibilityManagerService - private UiTestAutomationBridge mUiTestAutomationBridge; - - public InterrogationActivityTest() { - super(InterrogationActivity.class); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - mUiTestAutomationBridge = new UiTestAutomationBridge(); - mUiTestAutomationBridge.connect(); - mUiTestAutomationBridge.waitForIdle(IDLE_EVENT_TIME_DELTA_MILLIS, - GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS); - mUiTestAutomationBridge.executeCommandAndWaitForAccessibilityEvent(new Runnable() { - // wait for the first accessibility event - @Override - public void run() { - // bring up the activity - getActivity(); - } - }, - new Predicate<AccessibilityEvent>() { - @Override - public boolean apply(AccessibilityEvent event) { - return (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED - && event.getPackageName().equals(getActivity().getPackageName())); - } - }, - TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS); - } - - @Override - public void tearDown() throws Exception { - mUiTestAutomationBridge.disconnect(); - super.tearDown(); - } - - @LargeTest - public void testFindAccessibilityNodeInfoByViewId() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertNotNull(button); - assertEquals(0, button.getChildCount()); - - // bounds - Rect bounds = new Rect(); - button.getBoundsInParent(bounds); - assertEquals(0, bounds.left); - assertEquals(0, bounds.top); - assertEquals(160, bounds.right); - assertEquals(100, bounds.bottom); - - // char sequence attributes - assertEquals("com.android.frameworks.coretests", button.getPackageName()); - assertEquals("android.widget.Button", button.getClassName()); - assertEquals("Button5", button.getText()); - assertNull(button.getContentDescription()); - - // boolean attributes - assertTrue(button.isFocusable()); - assertTrue(button.isClickable()); - assertTrue(button.isEnabled()); - assertFalse(button.isFocused()); - assertTrue(button.isClickable()); - assertFalse(button.isPassword()); - assertFalse(button.isSelected()); - assertFalse(button.isCheckable()); - assertFalse(button.isChecked()); - - // actions - assertEquals(ACTION_FOCUS | ACTION_SELECT | ACTION_CLEAR_SELECTION, - button.getActions()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewId: " - + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testFindAccessibilityNodeInfoByViewText() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view by text - List<AccessibilityNodeInfo> buttons = mUiTestAutomationBridge - .findAccessibilityNodeInfosByTextInActiveWindow("butto"); - assertEquals(9, buttons.size()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewText: " - + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testFindAccessibilityNodeInfoByViewTextContentDescription() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view by text - List<AccessibilityNodeInfo> buttons = mUiTestAutomationBridge - .findAccessibilityNodeInfosByTextInActiveWindow("contentDescription"); - assertEquals(1, buttons.size()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewTextContentDescription: " - + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testTraverseAllViews() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // make list of expected nodes - List<String> classNameAndTextList = new ArrayList<String>(); - classNameAndTextList.add("android.widget.LinearLayout"); - classNameAndTextList.add("android.widget.LinearLayout"); - classNameAndTextList.add("android.widget.LinearLayout"); - classNameAndTextList.add("android.widget.LinearLayout"); - classNameAndTextList.add("android.widget.ButtonButton1"); - classNameAndTextList.add("android.widget.ButtonButton2"); - classNameAndTextList.add("android.widget.ButtonButton3"); - classNameAndTextList.add("android.widget.ButtonButton4"); - classNameAndTextList.add("android.widget.ButtonButton5"); - classNameAndTextList.add("android.widget.ButtonButton6"); - classNameAndTextList.add("android.widget.ButtonButton7"); - classNameAndTextList.add("android.widget.ButtonButton8"); - classNameAndTextList.add("android.widget.ButtonButton9"); - - AccessibilityNodeInfo root = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root); - assertNotNull("We must find the existing root.", root); - - Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>(); - fringe.add(root); - - // do a BFS traversal and check nodes - while (!fringe.isEmpty()) { - AccessibilityNodeInfo current = fringe.poll(); - - CharSequence className = current.getClassName(); - CharSequence text = current.getText(); - String receivedClassNameAndText = className.toString() - + ((text != null) ? text.toString() : ""); - String expectedClassNameAndText = classNameAndTextList.remove(0); - - assertEquals("Did not get the expected node info", - expectedClassNameAndText, receivedClassNameAndText); - - final int childCount = current.getChildCount(); - for (int i = 0; i < childCount; i++) { - AccessibilityNodeInfo child = current.getChild(i); - fringe.add(child); - } - } - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testTraverseAllViews: " + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testPerformAccessibilityActionFocus() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view and make sure it is not focused - AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isFocused()); - - // focus the view - assertTrue(button.performAction(ACTION_FOCUS)); - - // find the view again and make sure it is focused - button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertTrue(button.isFocused()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testPerformAccessibilityActionFocus: " + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testPerformAccessibilityActionClearFocus() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view and make sure it is not focused - AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isFocused()); - - // focus the view - assertTrue(button.performAction(ACTION_FOCUS)); - - // find the view again and make sure it is focused - button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertTrue(button.isFocused()); - - // unfocus the view - assertTrue(button.performAction(ACTION_CLEAR_FOCUS)); - - // find the view again and make sure it is not focused - button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isFocused()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testPerformAccessibilityActionClearFocus: " - + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testPerformAccessibilityActionSelect() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view and make sure it is not selected - AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isSelected()); - - // select the view - assertTrue(button.performAction(ACTION_SELECT)); - - // find the view again and make sure it is selected - button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertTrue(button.isSelected()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testPerformAccessibilityActionSelect: " + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testPerformAccessibilityActionClearSelection() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view and make sure it is not selected - AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isSelected()); - - // select the view - assertTrue(button.performAction(ACTION_SELECT)); - - // find the view again and make sure it is selected - button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertTrue(button.isSelected()); - - // unselect the view - assertTrue(button.performAction(ACTION_CLEAR_SELECTION)); - - // find the view again and make sure it is not selected - button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isSelected()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testPerformAccessibilityActionClearSelection: " - + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testAccessibilityEventGetSource() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view and make sure it is not focused - final AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertFalse(button.isFocused()); - - AccessibilityEvent event = mUiTestAutomationBridge - .executeCommandAndWaitForAccessibilityEvent(new Runnable() { - @Override - public void run() { - // focus the view - assertTrue(button.performAction(ACTION_FOCUS)); - } - }, - new Predicate<AccessibilityEvent>() { - @Override - public boolean apply(AccessibilityEvent event) { - return (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED - && event.getPackageName().equals(getActivity().getPackageName()) - && event.getText().get(0).equals(button.getText())); - } - }, - TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS); - - // check the last event - assertNotNull(event); - - // check that last event source - AccessibilityNodeInfo source = event.getSource(); - assertNotNull(source); - - // bounds - Rect buttonBounds = new Rect(); - button.getBoundsInParent(buttonBounds); - Rect sourceBounds = new Rect(); - source.getBoundsInParent(sourceBounds); - - assertEquals(buttonBounds.left, sourceBounds.left); - assertEquals(buttonBounds.right, sourceBounds.right); - assertEquals(buttonBounds.top, sourceBounds.top); - assertEquals(buttonBounds.bottom, sourceBounds.bottom); - - // char sequence attributes - assertEquals(button.getPackageName(), source.getPackageName()); - assertEquals(button.getClassName(), source.getClassName()); - assertEquals(button.getText(), source.getText()); - assertSame(button.getContentDescription(), source.getContentDescription()); - - // boolean attributes - assertSame(button.isFocusable(), source.isFocusable()); - assertSame(button.isClickable(), source.isClickable()); - assertSame(button.isEnabled(), source.isEnabled()); - assertNotSame(button.isFocused(), source.isFocused()); - assertSame(button.isLongClickable(), source.isLongClickable()); - assertSame(button.isPassword(), source.isPassword()); - assertSame(button.isSelected(), source.isSelected()); - assertSame(button.isCheckable(), source.isCheckable()); - assertSame(button.isChecked(), source.isChecked()); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testAccessibilityEventGetSource: " + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testObjectContract() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // find a view and make sure it is not focused - AccessibilityNodeInfo button = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); - assertNotNull(button); - AccessibilityNodeInfo parent = button.getParent(); - final int childCount = parent.getChildCount(); - for (int i = 0; i < childCount; i++) { - AccessibilityNodeInfo child = parent.getChild(i); - assertNotNull(child); - if (child.equals(button)) { - assertEquals("Equal objects must have same hasCode.", button.hashCode(), - child.hashCode()); - return; - } - } - fail("Parent's children do not have the info whose parent is the parent."); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testObjectContract: " + elapsedTimeMillis + "ms"); - } - } - } - - @LargeTest - public void testGetRootAccessibilityNodeInfoInActiveWindow() throws Exception { - final long startTimeMillis = SystemClock.uptimeMillis(); - try { - // get the root via the designated API - AccessibilityNodeInfo fetched = mUiTestAutomationBridge - .getRootAccessibilityNodeInfoInActiveWindow(); - assertNotNull(fetched); - - // get the root via traversal - AccessibilityNodeInfo expected = mUiTestAutomationBridge - .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root); - while (true) { - AccessibilityNodeInfo parent = expected.getParent(); - if (parent == null) { - break; - } - expected = parent; - } - assertNotNull(expected); - - assertEquals("The node with id \"root\" should be the root.", expected, fetched); - } finally { - if (DEBUG) { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - Log.i(LOG_TAG, "testGetRootAccessibilityNodeInfoInActiveWindow: " - + elapsedTimeMillis + "ms"); - } - } - } -} diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java deleted file mode 100644 index 84c9957..0000000 --- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.app.Notification; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.RegisteredServicesCache.ServiceInfo; -import android.content.pm.RegisteredServicesCacheListener; -import android.os.Bundle; -import android.os.Handler; -import android.os.UserHandle; -import android.test.AndroidTestCase; -import android.test.IsolatedContext; -import android.test.RenamingDelegatingContext; -import android.test.mock.MockContentResolver; -import android.test.mock.MockContext; -import android.test.mock.MockPackageManager; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; - -public class AccountManagerServiceTest extends AndroidTestCase { - private AccountManagerService mAms; - - @Override - protected void setUp() throws Exception { - final String filenamePrefix = "test."; - MockContentResolver resolver = new MockContentResolver(); - RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext( - new MyMockContext(), // The context that most methods are delegated to - getContext(), // The context that file methods are delegated to - filenamePrefix); - Context context = new IsolatedContext(resolver, targetContextWrapper); - setContext(context); - mAms = new MyAccountManagerService(getContext(), - new MyMockPackageManager(), new MockAccountAuthenticatorCache()); - } - - public class AccountSorter implements Comparator<Account> { - public int compare(Account object1, Account object2) { - if (object1 == object2) return 0; - if (object1 == null) return 1; - if (object2 == null) return -1; - int result = object1.type.compareTo(object2.type); - if (result != 0) return result; - return object1.name.compareTo(object2.name); - } - } - - public void testCheckAddAccount() throws Exception { - Account a11 = new Account("account1", "type1"); - Account a21 = new Account("account2", "type1"); - Account a31 = new Account("account3", "type1"); - Account a12 = new Account("account1", "type2"); - Account a22 = new Account("account2", "type2"); - Account a32 = new Account("account3", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - mAms.addAccount(a21, "p21", null); - mAms.addAccount(a22, "p22", null); - mAms.addAccount(a31, "p31", null); - mAms.addAccount(a32, "p32", null); - - Account[] accounts = mAms.getAccounts(null); - Arrays.sort(accounts, new AccountSorter()); - assertEquals(6, accounts.length); - assertEquals(a11, accounts[0]); - assertEquals(a21, accounts[1]); - assertEquals(a31, accounts[2]); - assertEquals(a12, accounts[3]); - assertEquals(a22, accounts[4]); - assertEquals(a32, accounts[5]); - - accounts = mAms.getAccounts("type1" ); - Arrays.sort(accounts, new AccountSorter()); - assertEquals(3, accounts.length); - assertEquals(a11, accounts[0]); - assertEquals(a21, accounts[1]); - assertEquals(a31, accounts[2]); - - mAms.removeAccountInternal(a21); - - accounts = mAms.getAccounts("type1" ); - Arrays.sort(accounts, new AccountSorter()); - assertEquals(2, accounts.length); - assertEquals(a11, accounts[0]); - assertEquals(a31, accounts[1]); - } - - public void testPasswords() throws Exception { - Account a11 = new Account("account1", "type1"); - Account a12 = new Account("account1", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - - assertEquals("p11", mAms.getPassword(a11)); - assertEquals("p12", mAms.getPassword(a12)); - - mAms.setPassword(a11, "p11b"); - - assertEquals("p11b", mAms.getPassword(a11)); - assertEquals("p12", mAms.getPassword(a12)); - } - - public void testUserdata() throws Exception { - Account a11 = new Account("account1", "type1"); - Bundle u11 = new Bundle(); - u11.putString("a", "a_a11"); - u11.putString("b", "b_a11"); - u11.putString("c", "c_a11"); - Account a12 = new Account("account1", "type2"); - Bundle u12 = new Bundle(); - u12.putString("a", "a_a12"); - u12.putString("b", "b_a12"); - u12.putString("c", "c_a12"); - mAms.addAccount(a11, "p11", u11); - mAms.addAccount(a12, "p12", u12); - - assertEquals("a_a11", mAms.getUserData(a11, "a")); - assertEquals("b_a11", mAms.getUserData(a11, "b")); - assertEquals("c_a11", mAms.getUserData(a11, "c")); - assertEquals("a_a12", mAms.getUserData(a12, "a")); - assertEquals("b_a12", mAms.getUserData(a12, "b")); - assertEquals("c_a12", mAms.getUserData(a12, "c")); - - mAms.setUserData(a11, "b", "b_a11b"); - mAms.setUserData(a12, "c", null); - - assertEquals("a_a11", mAms.getUserData(a11, "a")); - assertEquals("b_a11b", mAms.getUserData(a11, "b")); - assertEquals("c_a11", mAms.getUserData(a11, "c")); - assertEquals("a_a12", mAms.getUserData(a12, "a")); - assertEquals("b_a12", mAms.getUserData(a12, "b")); - assertNull(mAms.getUserData(a12, "c")); - } - - public void testAuthtokens() throws Exception { - Account a11 = new Account("account1", "type1"); - Account a12 = new Account("account1", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - - mAms.setAuthToken(a11, "att1", "a11_att1"); - mAms.setAuthToken(a11, "att2", "a11_att2"); - mAms.setAuthToken(a11, "att3", "a11_att3"); - mAms.setAuthToken(a12, "att1", "a12_att1"); - mAms.setAuthToken(a12, "att2", "a12_att2"); - mAms.setAuthToken(a12, "att3", "a12_att3"); - - assertEquals("a11_att1", mAms.peekAuthToken(a11, "att1")); - assertEquals("a11_att2", mAms.peekAuthToken(a11, "att2")); - assertEquals("a11_att3", mAms.peekAuthToken(a11, "att3")); - assertEquals("a12_att1", mAms.peekAuthToken(a12, "att1")); - assertEquals("a12_att2", mAms.peekAuthToken(a12, "att2")); - assertEquals("a12_att3", mAms.peekAuthToken(a12, "att3")); - - mAms.setAuthToken(a11, "att3", "a11_att3b"); - mAms.invalidateAuthToken(a12.type, "a12_att2"); - - assertEquals("a11_att1", mAms.peekAuthToken(a11, "att1")); - assertEquals("a11_att2", mAms.peekAuthToken(a11, "att2")); - assertEquals("a11_att3b", mAms.peekAuthToken(a11, "att3")); - assertEquals("a12_att1", mAms.peekAuthToken(a12, "att1")); - assertNull(mAms.peekAuthToken(a12, "att2")); - assertEquals("a12_att3", mAms.peekAuthToken(a12, "att3")); - - assertNull(mAms.peekAuthToken(a12, "att2")); - } - - static public class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache { - private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices; - - public MockAccountAuthenticatorCache() { - mServices = new ArrayList<ServiceInfo<AuthenticatorDescription>>(); - AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0); - AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0); - mServices.add(new ServiceInfo<AuthenticatorDescription>(d1, null, 0)); - mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, 0)); - } - - @Override - public ServiceInfo<AuthenticatorDescription> getServiceInfo( - AuthenticatorDescription type, int userId) { - for (ServiceInfo<AuthenticatorDescription> service : mServices) { - if (service.type.equals(type)) { - return service; - } - } - return null; - } - - @Override - public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices(int userId) { - return mServices; - } - - @Override - public void dump( - final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) { - } - - @Override - public void setListener( - final RegisteredServicesCacheListener<AuthenticatorDescription> listener, - final Handler handler) { - } - - @Override - public void invalidateCache(int userId) { - } - } - - static public class MyMockContext extends MockContext { - @Override - public int checkCallingOrSelfPermission(final String permission) { - return PackageManager.PERMISSION_GRANTED; - } - } - - static public class MyMockPackageManager extends MockPackageManager { - @Override - public int checkSignatures(final int uid1, final int uid2) { - return PackageManager.SIGNATURE_MATCH; - } - } - - static public class MyAccountManagerService extends AccountManagerService { - public MyAccountManagerService(Context context, PackageManager packageManager, - IAccountAuthenticatorCache authenticatorCache) { - super(context, packageManager, authenticatorCache); - } - - @Override - protected void installNotification(final int notificationId, final Notification n, UserHandle user) { - } - - @Override - protected void cancelNotification(final int id, UserHandle user) { - } - } -} diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java deleted file mode 100644 index 4d3b144..0000000 --- a/core/tests/coretests/src/android/app/SearchablesTest.java +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app; - -import android.app.SearchManager; -import android.app.SearchableInfo; -import android.app.SearchableInfo.ActionKeyInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.content.res.XmlResourceParser; -import android.os.RemoteException; -import android.server.search.Searchables; -import android.test.AndroidTestCase; -import android.test.MoreAsserts; -import android.test.mock.MockContext; -import android.test.mock.MockPackageManager; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.KeyEvent; - -import java.util.ArrayList; -import java.util.List; - -/** - * To launch this test from the command line: - * - * adb shell am instrument -w \ - * -e class com.android.unit_tests.SearchablesTest \ - * com.android.unit_tests/android.test.InstrumentationTestRunner - */ -@SmallTest -public class SearchablesTest extends AndroidTestCase { - - /* - * SearchableInfo tests - * Mock the context so I can provide very specific input data - * Confirm OK with "zero" searchables - * Confirm "good" metadata read properly - * Confirm "bad" metadata skipped properly - * Confirm ordering of searchables - * Confirm "good" actionkeys - * confirm "bad" actionkeys are rejected - * confirm XML ordering enforced (will fail today - bug in SearchableInfo) - * findActionKey works - * getIcon works - */ - - /** - * Test that non-searchable activities return no searchable info (this would typically - * trigger the use of the default searchable e.g. contacts) - */ - public void testNonSearchable() { - // test basic array & hashmap - Searchables searchables = new Searchables(mContext, 0); - searchables.buildSearchableList(); - - // confirm that we return null for non-searchy activities - ComponentName nonActivity = new ComponentName( - "com.android.frameworks.coretests", - "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY"); - SearchableInfo si = searchables.getSearchableInfo(nonActivity); - assertNull(si); - } - - /** - * This is an attempt to run the searchable info list with a mocked context. Here are some - * things I'd like to test. - * - * Confirm OK with "zero" searchables - * Confirm "good" metadata read properly - * Confirm "bad" metadata skipped properly - * Confirm ordering of searchables - * Confirm "good" actionkeys - * confirm "bad" actionkeys are rejected - * confirm XML ordering enforced (will fail today - bug in SearchableInfo) - * findActionKey works - * getIcon works - - */ - public void testSearchablesListReal() { - MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); - MyMockContext mockContext = new MyMockContext(mContext, mockPM); - - // build item list with real-world source data - mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH); - Searchables searchables = new Searchables(mockContext, 0); - searchables.buildSearchableList(); - // tests with "real" searchables (deprecate, this should be a unit test) - ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); - int count = searchablesList.size(); - assertTrue(count >= 1); // this isn't really a unit test - checkSearchables(searchablesList); - ArrayList<SearchableInfo> global = searchables.getSearchablesInGlobalSearchList(); - checkSearchables(global); - } - - /** - * This round of tests confirms good operations with "zero" searchables found - */ - public void testSearchablesListEmpty() { - MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); - MyMockContext mockContext = new MyMockContext(mContext, mockPM); - - mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO); - Searchables searchables = new Searchables(mockContext, 0); - searchables.buildSearchableList(); - ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); - assertNotNull(searchablesList); - MoreAsserts.assertEmpty(searchablesList); - ArrayList<SearchableInfo> global = searchables.getSearchablesInGlobalSearchList(); - MoreAsserts.assertEmpty(global); - } - - /** - * Generic health checker for an array of searchables. - * - * This is designed to pass for any semi-legal searchable, without knowing much about - * the format of the underlying data. It's fairly easy for a non-compliant application - * to provide meta-data that will pass here (e.g. a non-existent suggestions authority). - * - * @param searchables The list of searchables to examine. - */ - private void checkSearchables(ArrayList<SearchableInfo> searchablesList) { - assertNotNull(searchablesList); - int count = searchablesList.size(); - for (int ii = 0; ii < count; ii++) { - SearchableInfo si = searchablesList.get(ii); - checkSearchable(si); - } - } - - private void checkSearchable(SearchableInfo si) { - assertNotNull(si); - assertTrue(si.getLabelId() != 0); // This must be a useable string - assertNotEmpty(si.getSearchActivity().getClassName()); - assertNotEmpty(si.getSearchActivity().getPackageName()); - if (si.getSuggestAuthority() != null) { - // The suggestion fields are largely optional, so we'll just confirm basic health - assertNotEmpty(si.getSuggestAuthority()); - assertNullOrNotEmpty(si.getSuggestPath()); - assertNullOrNotEmpty(si.getSuggestSelection()); - assertNullOrNotEmpty(si.getSuggestIntentAction()); - assertNullOrNotEmpty(si.getSuggestIntentData()); - } - /* Add a way to get the entire action key list, then explicitly test its elements */ - /* For now, test the most common action key (CALL) */ - ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL); - if (ai != null) { - assertEquals(ai.getKeyCode(), KeyEvent.KEYCODE_CALL); - // one of these three fields must be non-null & non-empty - boolean m1 = (ai.getQueryActionMsg() != null) && (ai.getQueryActionMsg().length() > 0); - boolean m2 = (ai.getSuggestActionMsg() != null) && (ai.getSuggestActionMsg().length() > 0); - boolean m3 = (ai.getSuggestActionMsgColumn() != null) && - (ai.getSuggestActionMsgColumn().length() > 0); - assertTrue(m1 || m2 || m3); - } - - /* - * Find ways to test these: - * - * private int mSearchMode - * private Drawable mIcon - */ - - /* - * Explicitly not tested here: - * - * Can be null, so not much to see: - * public String mSearchHint - * private String mZeroQueryBanner - * - * To be deprecated/removed, so don't bother: - * public boolean mFilterMode - * public boolean mQuickStart - * private boolean mIconResized - * private int mIconResizeWidth - * private int mIconResizeHeight - * - * All of these are "internal" working variables, not part of any contract - * private ActivityInfo mActivityInfo - * private Rect mTempRect - * private String mSuggestProviderPackage - * private String mCacheActivityContext - */ - } - - /** - * Combo assert for "string not null and not empty" - */ - private void assertNotEmpty(final String s) { - assertNotNull(s); - MoreAsserts.assertNotEqual(s, ""); - } - - /** - * Combo assert for "string null or (not null and not empty)" - */ - private void assertNullOrNotEmpty(final String s) { - if (s != null) { - MoreAsserts.assertNotEqual(s, ""); - } - } - - /** - * This is a mock for context. Used to perform a true unit test on SearchableInfo. - * - */ - private class MyMockContext extends MockContext { - - protected Context mRealContext; - protected PackageManager mPackageManager; - - /** - * Constructor. - * - * @param realContext Please pass in a real context for some pass-throughs to function. - */ - MyMockContext(Context realContext, PackageManager packageManager) { - mRealContext = realContext; - mPackageManager = packageManager; - } - - /** - * Resources. Pass through for now. - */ - @Override - public Resources getResources() { - return mRealContext.getResources(); - } - - /** - * Package manager. Pass through for now. - */ - @Override - public PackageManager getPackageManager() { - return mPackageManager; - } - - /** - * Package manager. Pass through for now. - */ - @Override - public Context createPackageContext(String packageName, int flags) - throws PackageManager.NameNotFoundException { - return mRealContext.createPackageContext(packageName, flags); - } - - /** - * Message broadcast. Pass through for now. - */ - @Override - public void sendBroadcast(Intent intent) { - mRealContext.sendBroadcast(intent); - } - } - -/** - * This is a mock for package manager. Used to perform a true unit test on SearchableInfo. - * - */ - private class MyMockPackageManager extends MockPackageManager { - - public final static int SEARCHABLES_PASSTHROUGH = 0; - public final static int SEARCHABLES_MOCK_ZERO = 1; - public final static int SEARCHABLES_MOCK_ONEGOOD = 2; - public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3; - - protected PackageManager mRealPackageManager; - protected int mSearchablesMode; - - public MyMockPackageManager(PackageManager realPM) { - mRealPackageManager = realPM; - mSearchablesMode = SEARCHABLES_PASSTHROUGH; - } - - /** - * Set the mode for various tests. - */ - public void setSearchablesMode(int newMode) { - switch (newMode) { - case SEARCHABLES_PASSTHROUGH: - case SEARCHABLES_MOCK_ZERO: - mSearchablesMode = newMode; - break; - - default: - throw new UnsupportedOperationException(); - } - } - - /** - * Find activities that support a given intent. - * - * Retrieve all activities that can be performed for the given intent. - * - * @param intent The desired intent as per resolveActivity(). - * @param flags Additional option flags. The most important is - * MATCH_DEFAULT_ONLY, to limit the resolution to only - * those activities that support the CATEGORY_DEFAULT. - * - * @return A List<ResolveInfo> containing one entry for each matching - * Activity. These are ordered from best to worst match -- that - * is, the first item in the list is what is returned by - * resolveActivity(). If there are no matching activities, an empty - * list is returned. - */ - @Override - public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { - assertNotNull(intent); - assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH) - || intent.getAction().equals(Intent.ACTION_WEB_SEARCH) - || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); - switch (mSearchablesMode) { - case SEARCHABLES_PASSTHROUGH: - return mRealPackageManager.queryIntentActivities(intent, flags); - case SEARCHABLES_MOCK_ZERO: - return null; - default: - throw new UnsupportedOperationException(); - } - } - - @Override - public ResolveInfo resolveActivity(Intent intent, int flags) { - assertNotNull(intent); - assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH) - || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); - switch (mSearchablesMode) { - case SEARCHABLES_PASSTHROUGH: - return mRealPackageManager.resolveActivity(intent, flags); - case SEARCHABLES_MOCK_ZERO: - return null; - default: - throw new UnsupportedOperationException(); - } - } - - /** - * Retrieve an XML file from a package. This is a low-level API used to - * retrieve XML meta data. - * - * @param packageName The name of the package that this xml is coming from. - * Can not be null. - * @param resid The resource identifier of the desired xml. Can not be 0. - * @param appInfo Overall information about <var>packageName</var>. This - * may be null, in which case the application information will be retrieved - * for you if needed; if you already have this information around, it can - * be much more efficient to supply it here. - * - * @return Returns an XmlPullParser allowing you to parse out the XML - * data. Returns null if the xml resource could not be found for any - * reason. - */ - @Override - public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) { - assertNotNull(packageName); - MoreAsserts.assertNotEqual(packageName, ""); - MoreAsserts.assertNotEqual(resid, 0); - switch (mSearchablesMode) { - case SEARCHABLES_PASSTHROUGH: - return mRealPackageManager.getXml(packageName, resid, appInfo); - case SEARCHABLES_MOCK_ZERO: - default: - throw new UnsupportedOperationException(); - } - } - - /** - * Find a single content provider by its base path name. - * - * @param name The name of the provider to find. - * @param flags Additional option flags. Currently should always be 0. - * - * @return ContentProviderInfo Information about the provider, if found, - * else null. - */ - @Override - public ProviderInfo resolveContentProvider(String name, int flags) { - assertNotNull(name); - MoreAsserts.assertNotEqual(name, ""); - assertEquals(flags, 0); - switch (mSearchablesMode) { - case SEARCHABLES_PASSTHROUGH: - return mRealPackageManager.resolveContentProvider(name, flags); - case SEARCHABLES_MOCK_ZERO: - default: - throw new UnsupportedOperationException(); - } - } - - /** - * Get the activity information for a particular activity. - * - * @param name The name of the activity to find. - * @param flags Additional option flags. - * - * @return ActivityInfo Information about the activity, if found, else null. - */ - @Override - public ActivityInfo getActivityInfo(ComponentName name, int flags) - throws NameNotFoundException { - assertNotNull(name); - MoreAsserts.assertNotEqual(name, ""); - switch (mSearchablesMode) { - case SEARCHABLES_PASSTHROUGH: - return mRealPackageManager.getActivityInfo(name, flags); - case SEARCHABLES_MOCK_ZERO: - throw new NameNotFoundException(); - default: - throw new UnsupportedOperationException(); - } - } - - @Override - public int checkPermission(String permName, String pkgName) { - assertNotNull(permName); - assertNotNull(pkgName); - switch (mSearchablesMode) { - case SEARCHABLES_PASSTHROUGH: - return mRealPackageManager.checkPermission(permName, pkgName); - case SEARCHABLES_MOCK_ZERO: - return PackageManager.PERMISSION_DENIED; - default: - throw new UnsupportedOperationException(); - } - } - } -} - diff --git a/core/tests/coretests/src/android/content/ObserverNodeTest.java b/core/tests/coretests/src/android/content/ObserverNodeTest.java deleted file mode 100644 index 1acff9c..0000000 --- a/core/tests/coretests/src/android/content/ObserverNodeTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import java.util.ArrayList; - -import android.content.ContentService.ObserverCall; -import android.content.ContentService.ObserverNode; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.os.UserHandle; -import android.test.AndroidTestCase; - -public class ObserverNodeTest extends AndroidTestCase { - static class TestObserver extends ContentObserver { - public TestObserver() { - super(new Handler()); - } - } - - public void testUri() { - final int myUserHandle = UserHandle.myUserId(); - - ObserverNode root = new ObserverNode(""); - Uri[] uris = new Uri[] { - Uri.parse("content://c/a/"), - Uri.parse("content://c/"), - Uri.parse("content://x/"), - Uri.parse("content://c/b/"), - Uri.parse("content://c/a/a1/1/"), - Uri.parse("content://c/a/a1/2/"), - Uri.parse("content://c/b/1/"), - Uri.parse("content://c/b/2/"), - }; - - int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3}; - - // special case - root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, - 0, 0, myUserHandle); - for(int i = 1; i < uris.length; i++) { - root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, - 0, 0, myUserHandle); - } - - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); - - for (int i = nums.length - 1; i >=0; --i) { - root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); - assertEquals(nums[i], calls.size()); - calls.clear(); - } - } - - public void testUriNotNotify() { - final int myUserHandle = UserHandle.myUserId(); - - ObserverNode root = new ObserverNode(""); - Uri[] uris = new Uri[] { - Uri.parse("content://c/"), - Uri.parse("content://x/"), - Uri.parse("content://c/a/"), - Uri.parse("content://c/b/"), - Uri.parse("content://c/a/1/"), - Uri.parse("content://c/a/2/"), - Uri.parse("content://c/b/1/"), - Uri.parse("content://c/b/2/"), - }; - int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1}; - - for(int i = 0; i < uris.length; i++) { - root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, - 0, 0, myUserHandle); - } - - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); - - for (int i = uris.length - 1; i >=0; --i) { - root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); - assertEquals(nums[i], calls.size()); - calls.clear(); - } - } -} diff --git a/core/tests/coretests/src/android/content/SyncOperationTest.java b/core/tests/coretests/src/android/content/SyncOperationTest.java deleted file mode 100644 index 910c721..0000000 --- a/core/tests/coretests/src/android/content/SyncOperationTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.os.Bundle; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -/** - * You can run those tests with: - * - * adb shell am instrument - * -e debug false - * -w - * -e class android.content.SyncOperationTest com.android.frameworks.coretests/android.test.InstrumentationTestRunner - */ - -public class SyncOperationTest extends AndroidTestCase { - - @SmallTest - public void testToKey() { - Account account1 = new Account("account1", "type1"); - Account account2 = new Account("account2", "type2"); - - Bundle b1 = new Bundle(); - Bundle b2 = new Bundle(); - b2.putBoolean("b2", true); - - SyncOperation op1 = new SyncOperation(account1, 0, - 1, - "authority1", - b1, - 100, - 1000, - 10000, - false); - - // Same as op1 but different time infos - SyncOperation op2 = new SyncOperation(account1, 0, - 1, - "authority1", - b1, - 200, - 2000, - 20000, - false); - - // Same as op1 but different authority - SyncOperation op3 = new SyncOperation(account1, 0, - 1, - "authority2", - b1, - 100, - 1000, - 10000, - false); - - // Same as op1 but different account - SyncOperation op4 = new SyncOperation(account2, 0, - 1, - "authority1", - b1, - 100, - 1000, - 10000, - false); - - // Same as op1 but different bundle - SyncOperation op5 = new SyncOperation(account1, 0, - 1, - "authority1", - b2, - 100, - 1000, - 10000, - false); - - assertEquals(op1.key, op2.key); - assertNotSame(op1.key, op3.key); - assertNotSame(op1.key, op4.key); - assertNotSame(op1.key, op5.key); - } -} diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java deleted file mode 100644 index 2add623..0000000 --- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import com.android.internal.os.AtomicFile; - -import android.accounts.Account; -import android.os.Bundle; -import android.test.AndroidTestCase; -import android.test.RenamingDelegatingContext; -import android.test.mock.MockContentResolver; -import android.test.mock.MockContext; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; - -import java.io.File; -import java.io.FileOutputStream; -import java.util.List; - -public class SyncStorageEngineTest extends AndroidTestCase { - - private File getSyncDir() { - return new File(new File(getContext().getFilesDir(), "system"), "sync"); - } - - /** - * Test that we handle the case of a history row being old enough to purge before the - * correcponding sync is finished. This can happen if the clock changes while we are syncing. - * - */ - // TODO: this test causes AidlTest to fail. Omit for now - // @SmallTest - public void testPurgeActiveSync() throws Exception { - final Account account = new Account("a@example.com", "example.type"); - final String authority = "testprovider"; - - MockContentResolver mockResolver = new MockContentResolver(); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance( - new TestContext(mockResolver, getContext())); - - long time0 = 1000; - long historyId = engine.insertStartSyncEvent( - account, 0, authority, time0, SyncStorageEngine.SOURCE_LOCAL, - false /* initialization */); - long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2; - engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0); - } - - /** - * Test that we can create, remove and retrieve periodic syncs - */ - @MediumTest - public void testPeriodics() throws Exception { - final Account account1 = new Account("a@example.com", "example.type"); - final Account account2 = new Account("b@example.com", "example.type.2"); - final String authority = "testprovider"; - final Bundle extras1 = new Bundle(); - extras1.putString("a", "1"); - final Bundle extras2 = new Bundle(); - extras2.putString("a", "2"); - final int period1 = 200; - final int period2 = 1000; - - PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1); - PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1); - PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2); - PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2); - - MockContentResolver mockResolver = new MockContentResolver(); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance( - new TestContext(mockResolver, getContext())); - - removePeriodicSyncs(engine, account1, 0, authority); - removePeriodicSyncs(engine, account2, 0, authority); - removePeriodicSyncs(engine, account1, 1, authority); - - // this should add two distinct periodic syncs for account1 and one for account2 - engine.addPeriodicSync(sync1.account, 0, sync1.authority, sync1.extras, sync1.period); - engine.addPeriodicSync(sync2.account, 0, sync2.authority, sync2.extras, sync2.period); - engine.addPeriodicSync(sync3.account, 0, sync3.authority, sync3.extras, sync3.period); - engine.addPeriodicSync(sync4.account, 0, sync4.authority, sync4.extras, sync4.period); - // add a second user - engine.addPeriodicSync(sync2.account, 1, sync2.authority, sync2.extras, sync2.period); - - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority); - - assertEquals(2, syncs.size()); - - assertEquals(sync1, syncs.get(0)); - assertEquals(sync3, syncs.get(1)); - - engine.removePeriodicSync(sync1.account, 0, sync1.authority, sync1.extras); - - syncs = engine.getPeriodicSyncs(account1, 0, authority); - assertEquals(1, syncs.size()); - assertEquals(sync3, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(account2, 0, authority); - assertEquals(1, syncs.size()); - assertEquals(sync4, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(sync2.account, 1, sync2.authority); - assertEquals(1, syncs.size()); - assertEquals(sync2, syncs.get(0)); - } - - private void removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, - String authority) { - engine.setIsSyncable(account, userId, authority, - engine.getIsSyncable(account, 0, authority)); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, userId, authority); - for (PeriodicSync sync : syncs) { - engine.removePeriodicSync(sync.account, userId, sync.authority, sync.extras); - } - } - - @LargeTest - public void testAuthorityPersistence() throws Exception { - final Account account1 = new Account("a@example.com", "example.type"); - final Account account2 = new Account("b@example.com", "example.type.2"); - final String authority1 = "testprovider1"; - final String authority2 = "testprovider2"; - final Bundle extras1 = new Bundle(); - extras1.putString("a", "1"); - final Bundle extras2 = new Bundle(); - extras2.putString("a", "2"); - extras2.putLong("b", 2); - extras2.putInt("c", 1); - extras2.putBoolean("d", true); - extras2.putDouble("e", 1.2); - extras2.putFloat("f", 4.5f); - extras2.putParcelable("g", account1); - final int period1 = 200; - final int period2 = 1000; - - PeriodicSync sync1 = new PeriodicSync(account1, authority1, extras1, period1); - PeriodicSync sync2 = new PeriodicSync(account1, authority1, extras2, period1); - PeriodicSync sync3 = new PeriodicSync(account1, authority2, extras1, period1); - PeriodicSync sync4 = new PeriodicSync(account1, authority2, extras2, period2); - PeriodicSync sync5 = new PeriodicSync(account2, authority1, extras1, period1); - - MockContentResolver mockResolver = new MockContentResolver(); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance( - new TestContext(mockResolver, getContext())); - - removePeriodicSyncs(engine, account1, 0, authority1); - removePeriodicSyncs(engine, account2, 0, authority1); - removePeriodicSyncs(engine, account1, 0, authority2); - removePeriodicSyncs(engine, account2, 0, authority2); - - engine.setMasterSyncAutomatically(false, 0); - - engine.setIsSyncable(account1, 0, authority1, 1); - engine.setSyncAutomatically(account1, 0, authority1, true); - - engine.setIsSyncable(account2, 0, authority1, 1); - engine.setSyncAutomatically(account2, 0, authority1, true); - - engine.setIsSyncable(account1, 0, authority2, 1); - engine.setSyncAutomatically(account1, 0, authority2, false); - - engine.setIsSyncable(account2, 0, authority2, 0); - engine.setSyncAutomatically(account2, 0, authority2, true); - - engine.addPeriodicSync(sync1.account, 0, sync1.authority, sync1.extras, sync1.period); - engine.addPeriodicSync(sync2.account, 0, sync2.authority, sync2.extras, sync2.period); - engine.addPeriodicSync(sync3.account, 0, sync3.authority, sync3.extras, sync3.period); - engine.addPeriodicSync(sync4.account, 0, sync4.authority, sync4.extras, sync4.period); - engine.addPeriodicSync(sync5.account, 0, sync5.authority, sync5.extras, sync5.period); - - engine.writeAllState(); - engine.clearAndReadState(); - - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority1); - assertEquals(2, syncs.size()); - assertEquals(sync1, syncs.get(0)); - assertEquals(sync2, syncs.get(1)); - - syncs = engine.getPeriodicSyncs(account1, 0, authority2); - assertEquals(2, syncs.size()); - assertEquals(sync3, syncs.get(0)); - assertEquals(sync4, syncs.get(1)); - - syncs = engine.getPeriodicSyncs(account2, 0, authority1); - assertEquals(1, syncs.size()); - assertEquals(sync5, syncs.get(0)); - - assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1)); - assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1)); - assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2)); - assertEquals(true, engine.getSyncAutomatically(account2, 0, authority2)); - - assertEquals(1, engine.getIsSyncable(account1, 0, authority1)); - assertEquals(1, engine.getIsSyncable(account2, 0, authority1)); - assertEquals(1, engine.getIsSyncable(account1, 0, authority2)); - assertEquals(0, engine.getIsSyncable(account2, 0, authority2)); - } - - @MediumTest - public void testAuthorityParsing() throws Exception { - final Account account = new Account("account1", "type1"); - final String authority1 = "auth1"; - final String authority2 = "auth2"; - final String authority3 = "auth3"; - final Bundle extras = new Bundle(); - PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, (long) (60 * 60 * 24)); - PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, (long) (60 * 60 * 24)); - PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, (long) (60 * 60 * 24)); - PeriodicSync sync1s = new PeriodicSync(account, authority1, extras, 1000); - PeriodicSync sync2s = new PeriodicSync(account, authority2, extras, 1000); - PeriodicSync sync3s = new PeriodicSync(account, authority3, extras, 1000); - - MockContentResolver mockResolver = new MockContentResolver(); - - final TestContext testContext = new TestContext(mockResolver, getContext()); - - byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<accounts>\n" - + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" - + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n" - + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" - + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" - + "</accounts>\n").getBytes(); - - File syncDir = getSyncDir(); - syncDir.mkdirs(); - AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - FileOutputStream fos = accountInfoFile.startWrite(); - fos.write(accountsFileData); - accountInfoFile.finishWrite(fos); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, 0, authority1); - assertEquals(1, syncs.size()); - assertEquals(sync1, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(account, 0, authority2); - assertEquals(1, syncs.size()); - assertEquals(sync2, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(account, 0, authority3); - assertEquals(1, syncs.size()); - assertEquals(sync3, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(account, 1, authority3); - assertEquals(1, syncs.size()); - assertEquals(sync3, syncs.get(0)); - - accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<accounts version=\"2\">\n" - + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" - + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n" - + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" - + "</accounts>\n").getBytes(); - - accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - fos = accountInfoFile.startWrite(); - fos.write(accountsFileData); - accountInfoFile.finishWrite(fos); - - engine.clearAndReadState(); - - syncs = engine.getPeriodicSyncs(account, 0, authority1); - assertEquals(0, syncs.size()); - - syncs = engine.getPeriodicSyncs(account, 0, authority2); - assertEquals(0, syncs.size()); - - syncs = engine.getPeriodicSyncs(account, 0, authority3); - assertEquals(0, syncs.size()); - - accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<accounts version=\"2\">\n" - + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n" - + "<periodicSync period=\"1000\" />\n" - + "</authority>" - + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n" - + "<periodicSync period=\"1000\" />\n" - + "</authority>" - + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n" - + "<periodicSync period=\"1000\" />\n" - + "</authority>" - + "</accounts>\n").getBytes(); - - accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - fos = accountInfoFile.startWrite(); - fos.write(accountsFileData); - accountInfoFile.finishWrite(fos); - - engine.clearAndReadState(); - - syncs = engine.getPeriodicSyncs(account, 0, authority1); - assertEquals(1, syncs.size()); - assertEquals(sync1s, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(account, 0, authority2); - assertEquals(1, syncs.size()); - assertEquals(sync2s, syncs.get(0)); - - syncs = engine.getPeriodicSyncs(account, 0, authority3); - assertEquals(1, syncs.size()); - assertEquals(sync3s, syncs.get(0)); - } - - @MediumTest - public void testListenForTicklesParsing() throws Exception { - byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<accounts>\n" - + "<listenForTickles user=\"0\" enabled=\"false\" />" - + "<listenForTickles user=\"1\" enabled=\"true\" />" - + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" - + "<authority id=\"1\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" - + "</accounts>\n").getBytes(); - - MockContentResolver mockResolver = new MockContentResolver(); - final TestContext testContext = new TestContext(mockResolver, getContext()); - - File syncDir = getSyncDir(); - syncDir.mkdirs(); - AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - FileOutputStream fos = accountInfoFile.startWrite(); - fos.write(accountsFileData); - accountInfoFile.finishWrite(fos); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - - assertEquals(false, engine.getMasterSyncAutomatically(0)); - assertEquals(true, engine.getMasterSyncAutomatically(1)); - assertEquals(true, engine.getMasterSyncAutomatically(2)); - - } - - @MediumTest - public void testAuthorityRenaming() throws Exception { - final Account account1 = new Account("acc1", "type1"); - final Account account2 = new Account("acc2", "type2"); - final String authorityContacts = "contacts"; - final String authorityCalendar = "calendar"; - final String authorityOther = "other"; - final String authorityContactsNew = "com.android.contacts"; - final String authorityCalendarNew = "com.android.calendar"; - - MockContentResolver mockResolver = new MockContentResolver(); - - final TestContext testContext = new TestContext(mockResolver, getContext()); - - byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<accounts>\n" - + "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n" - + "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n" - + "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n" - + "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n" - + "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n" - + "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n" - + "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\"" - + " authority=\"com.android.calendar\" />\n" - + "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\"" - + " authority=\"com.android.contacts\" />\n" - + "</accounts>\n").getBytes(); - - File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); - syncDir.mkdirs(); - AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - FileOutputStream fos = accountInfoFile.startWrite(); - fos.write(accountsFileData); - accountInfoFile.finishWrite(fos); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - - assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityContacts)); - assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityCalendar)); - assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityOther)); - assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityContactsNew)); - assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityCalendarNew)); - - assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContacts)); - assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendar)); - assertEquals(true, engine.getSyncAutomatically(account2, 0, authorityOther)); - assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContactsNew)); - assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendarNew)); - } - - @SmallTest - public void testSyncableMigration() throws Exception { - final Account account = new Account("acc", "type"); - - MockContentResolver mockResolver = new MockContentResolver(); - - final TestContext testContext = new TestContext(mockResolver, getContext()); - - byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<accounts>\n" - + "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n" - + "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n" - + "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\"" - + " authority=\"other3\" />\n" - + "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\"" - + " authority=\"other4\" />\n" - + "</accounts>\n").getBytes(); - - File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); - syncDir.mkdirs(); - AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - FileOutputStream fos = accountInfoFile.startWrite(); - fos.write(accountsFileData); - accountInfoFile.finishWrite(fos); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - - assertEquals(-1, engine.getIsSyncable(account, 0, "other1")); - assertEquals(1, engine.getIsSyncable(account, 0, "other2")); - assertEquals(0, engine.getIsSyncable(account, 0, "other3")); - assertEquals(1, engine.getIsSyncable(account, 0, "other4")); - } -} - -class TestContext extends ContextWrapper { - - ContentResolver mResolver; - - private final Context mRealContext; - - public TestContext(ContentResolver resolver, Context realContext) { - super(new RenamingDelegatingContext(new MockContext(), realContext, "test.")); - mRealContext = realContext; - mResolver = resolver; - } - - @Override - public File getFilesDir() { - return mRealContext.getFilesDir(); - } - - @Override - public void enforceCallingOrSelfPermission(String permission, String message) { - } - - @Override - public void sendBroadcast(Intent intent) { - } - - @Override - public ContentResolver getContentResolver() { - return mResolver; - } -} diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java index 6edd2dc..131651a 100644 --- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java +++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java @@ -132,6 +132,42 @@ public class SettingsProviderTest extends AndroidTestCase { } @MediumTest + public void testSettingsChangeForOtherUser() { + UserManager um = (UserManager) getContext().getSystemService(Context.USER_SERVICE); + ContentResolver r = getContext().getContentResolver(); + + // Make sure there's an owner + assertTrue(findUser(um, UserHandle.USER_OWNER)); + + // create a new user to use for testing + UserInfo otherUser = um.createUser("TestUser1", UserInfo.FLAG_GUEST); + assertTrue(otherUser != null); + try { + assertNotSame("Current calling user id should not be the new guest user", + otherUser.id, UserHandle.getCallingUserId()); + + Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "gps"); + Settings.Secure.putStringForUser(r, + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "network", otherUser.id); + + assertEquals("gps", + Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); + assertEquals("network", Settings.Secure.getStringForUser( + r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, otherUser.id)); + + assertNotSame("Current calling user id should not be the new guest user", + otherUser.id, UserHandle.getCallingUserId()); + Settings.Secure.setLocationProviderEnabledForUser(r, "network", false, otherUser.id); + assertEquals("", Settings.Secure.getStringForUser( + r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, otherUser.id)); + + } finally { + // Tidy up + um.removeUser(otherUser.id); + } + } + + @MediumTest public void testRowNumberContentUri() { ContentResolver r = getContext().getContentResolver(); diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java new file mode 100644 index 0000000..5f36cfd --- /dev/null +++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text.format; + +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +public class DateFormatTest extends TestCase { + @SmallTest + public void testHasDesignator() throws Exception { + assertTrue(DateFormat.hasDesignator("hh:mm:ss", DateFormat.MINUTE)); + assertTrue(DateFormat.hasDesignator("myyyy", DateFormat.MINUTE)); + assertTrue(DateFormat.hasDesignator("mmm", DateFormat.MINUTE)); + + assertFalse(DateFormat.hasDesignator("hh:MM:ss", DateFormat.MINUTE)); + } + + @SmallTest + public void testHasDesignatorEscaped() throws Exception { + assertTrue(DateFormat.hasDesignator("hh:mm 'LOL'", DateFormat.MINUTE)); + + assertFalse(DateFormat.hasDesignator("hh:mm 'yyyy'", DateFormat.YEAR)); + } +} diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java new file mode 100644 index 0000000..cb468bc --- /dev/null +++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import junit.framework.TestCase; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Random; + +/** + * Tests for {@link LongSparseLongArray}. + */ +public class LongSparseLongArrayTest extends TestCase { + private static final String TAG = "LongSparseLongArrayTest"; + + public void testSimplePut() throws Exception { + final LongSparseLongArray array = new LongSparseLongArray(5); + for (int i = 0; i < 48; i++) { + final long value = 1 << i; + array.put(value, value); + } + for (int i = 0; i < 48; i++) { + final long value = 1 << i; + assertEquals(value, array.get(value, -1)); + assertEquals(-1, array.get(-value, -1)); + } + } + + public void testSimplePutBackwards() throws Exception { + final LongSparseLongArray array = new LongSparseLongArray(5); + for (int i = 47; i >= 0; i--) { + final long value = 1 << i; + array.put(value, value); + } + for (int i = 0; i < 48; i++) { + final long value = 1 << i; + assertEquals(value, array.get(value, -1)); + assertEquals(-1, array.get(-value, -1)); + } + } + + public void testMiddleInsert() throws Exception { + final LongSparseLongArray array = new LongSparseLongArray(5); + for (int i = 0; i < 48; i++) { + final long value = 1 << i; + array.put(value, value); + } + final long special = (1 << 24) + 5; + array.put(special, 1024); + for (int i = 0; i < 48; i++) { + final long value = 1 << i; + assertEquals(value, array.get(value, -1)); + assertEquals(-1, array.get(-value, -1)); + } + assertEquals(1024, array.get(special, -1)); + } + + public void testFuzz() throws Exception { + final Random r = new Random(); + + final HashMap<Long, Long> map = new HashMap<Long, Long>(); + final LongSparseLongArray array = new LongSparseLongArray(r.nextInt(128)); + + for (int i = 0; i < 10240; i++) { + if (r.nextBoolean()) { + final long key = r.nextLong(); + final long value = r.nextLong(); + map.put(key, value); + array.put(key, value); + } + if (r.nextBoolean() && map.size() > 0) { + final int index = r.nextInt(map.size()); + final long key = getKeyAtIndex(map, index); + map.remove(key); + array.delete(key); + } + } + + Log.d(TAG, "verifying a map with " + map.size() + " entries"); + + for (Map.Entry<Long, Long> e : map.entrySet()) { + final long key = e.getKey(); + final long value = e.getValue(); + assertEquals(value, array.get(key)); + } + } + + private static <E> E getKeyAtIndex(Map<E, ?> map, int index) { + final Iterator<E> keys = map.keySet().iterator(); + for (int i = 0; i < index; i++) { + keys.next(); + } + return keys.next(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java new file mode 100644 index 0000000..be7116d --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import junit.framework.TestCase; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayOutputStream; + +/** + * Tests for {@link FastXmlSerializer} + */ +public class FastXmlSerializerTest extends TestCase { + public void testEmptyText() throws Exception { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + final XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, "utf-8"); + out.startDocument(null, true); + out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + out.startTag(null, "string"); + out.attribute(null, "name", "meow"); + out.text(""); + out.endTag(null, "string"); + + out.endDocument(); + + assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<string name=\"meow\"></string>", stream.toString()); + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/IndentingPrintWriterTest.java b/core/tests/coretests/src/com/android/internal/util/IndentingPrintWriterTest.java new file mode 100644 index 0000000..6773612 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/IndentingPrintWriterTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; + +/** + * Tests for {@link IndentingPrintWriter}. + */ +public class IndentingPrintWriterTest extends TestCase { + + private ByteArrayOutputStream mStream; + private PrintWriter mWriter; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mStream = new ByteArrayOutputStream(); + mWriter = new PrintWriter(mStream); + } + + public void testMultipleIndents() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, " "); + + pw.print("Hello"); + pw.increaseIndent(); + pw.println(); + pw.print("World"); + pw.increaseIndent(); + pw.println(); + pw.print("And"); + pw.decreaseIndent(); + pw.println(); + pw.print("Goodbye"); + pw.decreaseIndent(); + pw.println(); + pw.print("World"); + pw.println(); + + pw.flush(); + assertEquals("Hello\n World\n And\n Goodbye\nWorld\n", mStream.toString()); + } + + public void testAdjustIndentAfterNewline() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, " "); + + pw.println("Hello"); + pw.increaseIndent(); + pw.println("World"); + + pw.flush(); + assertEquals("Hello\n World\n", mStream.toString()); + } + + public void testWrapping() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, "", 10); + + pw.print("dog "); + pw.print("cat "); + pw.print("cow "); + pw.print("meow "); + + pw.flush(); + assertEquals("dog cat \ncow meow ", mStream.toString()); + } + + public void testWrappingIndented() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, " ", 10); + + pw.increaseIndent(); + pw.print("dog "); + pw.print("meow "); + pw.print("a "); + pw.print("b "); + pw.print("cow "); + + pw.flush(); + assertEquals(" dog \n meow \n a b \n cow ", mStream.toString()); + } + + public void testWrappingEmbeddedNewlines() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, " ", 10); + + pw.increaseIndent(); + pw.print("Lorem ipsum \ndolor sit \namet, consectetur \nadipiscing elit."); + + pw.flush(); + assertEquals(" Lorem ip\n sum \n dolor si\n t \n amet, co\n" + + " nsectetu\n r \n adipisci\n ng elit.\n", mStream.toString()); + } + + public void testWrappingSingleGiant() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, " ", 10); + + pw.increaseIndent(); + pw.print("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + + pw.flush(); + assertEquals(" Lorem ip\n sum dolo\n r sit am\n et, cons\n" + + " ectetur \n adipisci\n ng elit.\n", mStream.toString()); + } + + public void testWrappingPrefixedGiant() throws Exception { + final IndentingPrintWriter pw = new IndentingPrintWriter(mWriter, " ", 10); + + pw.increaseIndent(); + pw.print("foo"); + pw.print("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + + pw.flush(); + assertEquals(" foo\n Lorem ip\n sum dolo\n r sit am\n et, cons\n" + + " ectetur \n adipisci\n ng elit.\n", mStream.toString()); + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java index 386a78d..a81bb4b 100644 --- a/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java @@ -134,7 +134,7 @@ public class ProcFileReaderTest extends AndroidTestCase { fail("somehow read a string value?"); } catch (IOException e) { // expected - assertTrue(e.getMessage().contains("end of stream")); + assertTrue(e.getMessage().contains("End of stream")); } } @@ -152,6 +152,20 @@ public class ProcFileReaderTest extends AndroidTestCase { } } + public void testOptionalLongs() throws Exception { + final ProcFileReader reader = buildReader("123 456\n789\n"); + + assertEquals(123L, reader.nextLong()); + assertEquals(456L, reader.nextOptionalLong(-1L)); + assertEquals(-1L, reader.nextOptionalLong(-1L)); + assertEquals(-1L, reader.nextOptionalLong(-1L)); + assertEquals(-1L, reader.nextOptionalLong(-1L)); + reader.finishLine(); + + assertEquals(789L, reader.nextOptionalLong(-1L)); + assertEquals(-1L, reader.nextOptionalLong(-1L)); + } + private static ProcFileReader buildReader(String string) throws IOException { return buildReader(string, 2048); } diff --git a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java index 418bbd6..2a2c24e 100644 --- a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java +++ b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java @@ -16,6 +16,9 @@ package com.android.internal.util; +import java.util.Collection; +import java.util.Iterator; + import android.os.Debug; import android.os.HandlerThread; import android.os.Looper; @@ -58,11 +61,28 @@ public class StateMachineTest extends TestCase { } } + private void dumpLogRecs(StateMachine sm) { + int size = sm.getLogRecSize(); + tlog("size=" + size + " count=" + sm.getLogRecCount()); + for (int i = 0; i < size; i++) { + LogRec lr = sm.getLogRec(i); + tlog(lr.toString()); + } + } + + private void dumpLogRecs(Collection<LogRec> clr) { + int size = clr.size(); + tlog("size=" + size); + for (LogRec lr : clr) { + tlog(lr.toString()); + } + } + /** * Tests {@link StateMachine#quit()}. */ class StateMachineQuitTest extends StateMachine { - Object mWaitUntilTestDone = new Object(); + Collection<LogRec> mLogRecs; StateMachineQuitTest(String name) { super(name); @@ -78,39 +98,32 @@ public class StateMachineTest extends TestCase { @Override public void onQuitting() { - Log.d(TAG, "onQuitting"); + log("onQuitting"); addLogRec(ON_QUITTING); + mLogRecs = mThisSm.copyLogRecs(); synchronized (mThisSm) { mThisSm.notifyAll(); } - - // Don't leave onQuitting before the test is done as everything is cleared - // including the log records. - synchronized (mWaitUntilTestDone) { - try { - mWaitUntilTestDone.wait(); - } catch(InterruptedException e) { - } - } } class S1 extends State { + @Override public void exit() { - Log.d(TAG, "S1.exit"); - addLogRec(EXIT, mS1); + log("S1.exit"); + addLogRec(EXIT); } @Override public boolean processMessage(Message message) { switch(message.what) { // Sleep and assume the other messages will be queued up. case TEST_CMD_1: { - Log.d(TAG, "TEST_CMD_1"); + log("TEST_CMD_1"); sleep(500); quit(); break; } default: { - Log.d(TAG, "default what=" + message.what); + log("default what=" + message.what); break; } } @@ -128,7 +141,7 @@ public class StateMachineTest extends TestCase { StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest"); smQuitTest.start(); - if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit E"); + if (smQuitTest.isDbg()) tlog("testStateMachineQuit E"); synchronized (smQuitTest) { @@ -141,38 +154,36 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled smQuitTest.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachineQuit: exception while waiting " + e.getMessage()); + tloge("testStateMachineQuit: exception while waiting " + e.getMessage()); } } - assertEquals(8, smQuitTest.getLogRecCount()); + dumpLogRecs(smQuitTest.mLogRecs); + assertEquals(8, smQuitTest.mLogRecs.size()); LogRec lr; - - for (int i = 0; i < 6; i++) { - lr = smQuitTest.getLogRec(i); - assertEquals(i+1, lr.getWhat()); + Iterator<LogRec> itr = smQuitTest.mLogRecs.iterator(); + for (int i = 1; i <= 6; i++) { + lr = itr.next(); + assertEquals(i, lr.getWhat()); assertEquals(smQuitTest.mS1, lr.getState()); assertEquals(smQuitTest.mS1, lr.getOriginalState()); } - lr = smQuitTest.getLogRec(6); + lr = itr.next(); assertEquals(EXIT, lr.getInfo()); assertEquals(smQuitTest.mS1, lr.getState()); - lr = smQuitTest.getLogRec(7); + lr = itr.next(); assertEquals(ON_QUITTING, lr.getInfo()); - synchronized (smQuitTest.mWaitUntilTestDone) { - smQuitTest.mWaitUntilTestDone.notifyAll(); - } - if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit X"); + if (smQuitTest.isDbg()) tlog("testStateMachineQuit X"); } /** * Tests {@link StateMachine#quitNow()} */ class StateMachineQuitNowTest extends StateMachine { - Object mWaitUntilTestDone = new Object(); + public Collection<LogRec> mLogRecs = null; StateMachineQuitNowTest(String name) { super(name); @@ -188,39 +199,34 @@ public class StateMachineTest extends TestCase { @Override public void onQuitting() { - Log.d(TAG, "onQuitting"); + log("onQuitting"); addLogRec(ON_QUITTING); + // Get a copy of the log records since we're quitting and they will disappear + mLogRecs = mThisSm.copyLogRecs(); + synchronized (mThisSm) { mThisSm.notifyAll(); } - - // Don't leave onQuitting before the test is done as everything is cleared - // including the log records. - synchronized (mWaitUntilTestDone) { - try { - mWaitUntilTestDone.wait(); - } catch(InterruptedException e) { - } - } } class S1 extends State { + @Override public void exit() { - Log.d(TAG, "S1.exit"); - addLogRec(EXIT, mS1); + log("S1.exit"); + addLogRec(EXIT); } @Override public boolean processMessage(Message message) { switch(message.what) { // Sleep and assume the other messages will be queued up. case TEST_CMD_1: { - Log.d(TAG, "TEST_CMD_1"); + log("TEST_CMD_1"); sleep(500); quitNow(); break; } default: { - Log.d(TAG, "default what=" + message.what); + log("default what=" + message.what); break; } } @@ -238,11 +244,11 @@ public class StateMachineTest extends TestCase { StateMachineQuitNowTest smQuitNowTest = new StateMachineQuitNowTest("smQuitNowTest"); smQuitNowTest.start(); - if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow E"); + if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow E"); synchronized (smQuitNowTest) { - // Send 6 messages but we'll QuitNow on the first so even though + // Send 6 message we'll QuitNow on the first even though // we send 6 only one will be processed. for (int i = 1; i <= 6; i++) { smQuitNowTest.sendMessage(smQuitNowTest.obtainMessage(i)); @@ -252,31 +258,27 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled smQuitNowTest.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachineQuitNow: exception while waiting " + e.getMessage()); + tloge("testStateMachineQuitNow: exception while waiting " + e.getMessage()); } } - // Only three records because we executed quitNow. - assertEquals(3, smQuitNowTest.getLogRecCount()); + tlog("testStateMachineQuiteNow: logRecs=" + smQuitNowTest.mLogRecs); + assertEquals(3, smQuitNowTest.mLogRecs.size()); - LogRec lr; - - lr = smQuitNowTest.getLogRec(0); + Iterator<LogRec> itr = smQuitNowTest.mLogRecs.iterator(); + LogRec lr = itr.next(); assertEquals(1, lr.getWhat()); assertEquals(smQuitNowTest.mS1, lr.getState()); assertEquals(smQuitNowTest.mS1, lr.getOriginalState()); - lr = smQuitNowTest.getLogRec(1); + lr = itr.next(); assertEquals(EXIT, lr.getInfo()); assertEquals(smQuitNowTest.mS1, lr.getState()); - lr = smQuitNowTest.getLogRec(2); + lr = itr.next(); assertEquals(ON_QUITTING, lr.getInfo()); - synchronized (smQuitNowTest.mWaitUntilTestDone) { - smQuitNowTest.mWaitUntilTestDone.notifyAll(); - } - if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow X"); + if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow X"); } /** @@ -303,39 +305,39 @@ public class StateMachineTest extends TestCase { @Override public void enter() { // Test transitions in enter on the initial state work - addLogRec(ENTER, mS1); + addLogRec(ENTER); transitionTo(mS2); - Log.d(TAG, "S1.enter"); + log("S1.enter"); } @Override public void exit() { - // Test that message is HSM_INIT_CMD - addLogRec(EXIT, mS1); - Log.d(TAG, "S1.exit"); + addLogRec(EXIT); + log("S1.exit"); } } class S2 extends State { @Override public void enter() { - addLogRec(ENTER, mS2); - Log.d(TAG, "S2.enter"); + addLogRec(ENTER); + log("S2.enter"); } @Override public void exit() { - addLogRec(EXIT, mS2); - assertEquals(TEST_CMD_1, getCurrentMessage().what); - // Test transition in exit work transitionTo(mS4); - Log.d(TAG, "S2.exit"); + + assertEquals(TEST_CMD_1, getCurrentMessage().what); + addLogRec(EXIT); + + log("S2.exit"); } @Override public boolean processMessage(Message message) { // Start a transition to S3 but it will be // changed to a transition to S4 in exit transitionTo(mS3); - Log.d(TAG, "S2.processMessage"); + log("S2.processMessage"); return HANDLED; } } @@ -343,28 +345,28 @@ public class StateMachineTest extends TestCase { class S3 extends State { @Override public void enter() { - addLogRec(ENTER, mS3); - Log.d(TAG, "S3.enter"); + addLogRec(ENTER); + log("S3.enter"); } @Override public void exit() { - addLogRec(EXIT, mS3); - Log.d(TAG, "S3.exit"); + addLogRec(EXIT); + log("S3.exit"); } } class S4 extends State { @Override public void enter() { - addLogRec(ENTER, mS4); + addLogRec(ENTER); // Test that we can do halting in an enter/exit transitionToHaltingState(); - Log.d(TAG, "S4.enter"); + log("S4.enter"); } @Override public void exit() { - addLogRec(EXIT, mS4); - Log.d(TAG, "S4.exit"); + addLogRec(EXIT); + log("S4.exit"); } } @@ -390,7 +392,7 @@ public class StateMachineTest extends TestCase { new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest"); smEnterExitTranstionToTest.start(); if (smEnterExitTranstionToTest.isDbg()) { - Log.d(TAG, "testStateMachineEnterExitTransitionToTest E"); + tlog("testStateMachineEnterExitTransitionToTest E"); } synchronized (smEnterExitTranstionToTest) { @@ -400,13 +402,14 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled smEnterExitTranstionToTest.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting " + tloge("testStateMachineEnterExitTransitionToTest: exception while waiting " + e.getMessage()); } } - assertEquals(smEnterExitTranstionToTest.getLogRecCount(), 9); + dumpLogRecs(smEnterExitTranstionToTest); + assertEquals(9, smEnterExitTranstionToTest.getLogRecCount()); LogRec lr; lr = smEnterExitTranstionToTest.getLogRec(0); @@ -425,29 +428,44 @@ public class StateMachineTest extends TestCase { assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); + assertEquals(smEnterExitTranstionToTest.mS3, lr.getDestState()); lr = smEnterExitTranstionToTest.getLogRec(4); - assertEquals(EXIT, lr.getInfo()); + assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); + assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); + assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); + assertEquals(EXIT, lr.getInfo()); lr = smEnterExitTranstionToTest.getLogRec(5); + assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(ENTER, lr.getInfo()); assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); + assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState()); + assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); lr = smEnterExitTranstionToTest.getLogRec(6); + assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(EXIT, lr.getInfo()); assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); + assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState()); + assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); lr = smEnterExitTranstionToTest.getLogRec(7); + assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(ENTER, lr.getInfo()); assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); + assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState()); + assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); lr = smEnterExitTranstionToTest.getLogRec(8); + assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(EXIT, lr.getInfo()); assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); + assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState()); if (smEnterExitTranstionToTest.isDbg()) { - Log.d(TAG, "testStateMachineEnterExitTransitionToTest X"); + tlog("testStateMachineEnterExitTransitionToTest X"); } } @@ -495,7 +513,7 @@ public class StateMachineTest extends TestCase { StateMachine0 sm0 = new StateMachine0("sm0"); sm0.start(); - if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 E"); + if (sm0.isDbg()) tlog("testStateMachine0 E"); synchronized (sm0) { // Send 6 messages @@ -507,13 +525,15 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm0.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine0: exception while waiting " + e.getMessage()); + tloge("testStateMachine0: exception while waiting " + e.getMessage()); } } assertEquals(6, sm0.getLogRecCount()); assertEquals(3, sm0.getLogRecSize()); + dumpLogRecs(sm0); + LogRec lr; lr = sm0.getLogRec(0); assertEquals(TEST_CMD_4, lr.getWhat()); @@ -530,7 +550,7 @@ public class StateMachineTest extends TestCase { assertEquals(sm0.mS1, lr.getState()); assertEquals(sm0.mS1, lr.getOriginalState()); - if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X"); + if (sm0.isDbg()) tlog("testStateMachine0 X"); } /** @@ -550,7 +570,7 @@ public class StateMachineTest extends TestCase { // Set the initial state setInitialState(mS1); - if (DBG) Log.d(TAG, "StateMachine1: ctor X"); + if (DBG) log("StateMachine1: ctor X"); } class S1 extends State { @@ -595,7 +615,7 @@ public class StateMachineTest extends TestCase { public void testStateMachine1() throws Exception { StateMachine1 sm1 = new StateMachine1("sm1"); sm1.start(); - if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E"); + if (sm1.isDbg()) tlog("testStateMachine1 E"); synchronized (sm1) { // Send two messages @@ -606,7 +626,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm1.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage()); + tloge("testStateMachine1: exception while waiting " + e.getMessage()); } } @@ -629,7 +649,7 @@ public class StateMachineTest extends TestCase { assertEquals(2, sm1.mEnterCount); assertEquals(2, sm1.mExitCount); - if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X"); + if (sm1.isDbg()) tlog("testStateMachine1 X"); } /** @@ -651,7 +671,7 @@ public class StateMachineTest extends TestCase { // Set the initial state setInitialState(mS1); - if (DBG) Log.d(TAG, "StateMachine2: ctor X"); + if (DBG) log("StateMachine2: ctor X"); } class S1 extends State { @@ -702,7 +722,7 @@ public class StateMachineTest extends TestCase { public void testStateMachine2() throws Exception { StateMachine2 sm2 = new StateMachine2("sm2"); sm2.start(); - if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 E"); + if (sm2.isDbg()) tlog("testStateMachine2 E"); synchronized (sm2) { // Send two messages @@ -713,7 +733,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm2.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine2: exception while waiting " + e.getMessage()); + tloge("testStateMachine2: exception while waiting " + e.getMessage()); } } @@ -739,7 +759,7 @@ public class StateMachineTest extends TestCase { assertTrue(sm2.mDidEnter); assertTrue(sm2.mDidExit); - if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 X"); + if (sm2.isDbg()) tlog("testStateMachine2 X"); } /** @@ -760,7 +780,7 @@ public class StateMachineTest extends TestCase { // Set the initial state will be the child setInitialState(mChildState); - if (DBG) Log.d(TAG, "StateMachine3: ctor X"); + if (DBG) log("StateMachine3: ctor X"); } class ParentState extends State { @@ -796,7 +816,7 @@ public class StateMachineTest extends TestCase { public void testStateMachine3() throws Exception { StateMachine3 sm3 = new StateMachine3("sm3"); sm3.start(); - if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 E"); + if (sm3.isDbg()) tlog("testStateMachine3 E"); synchronized (sm3) { // Send two messages @@ -807,7 +827,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm3.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine3: exception while waiting " + e.getMessage()); + tloge("testStateMachine3: exception while waiting " + e.getMessage()); } } @@ -824,7 +844,7 @@ public class StateMachineTest extends TestCase { assertEquals(sm3.mParentState, lr.getState()); assertEquals(sm3.mChildState, lr.getOriginalState()); - if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X"); + if (sm3.isDbg()) tlog("testStateMachine3 X"); } /** @@ -847,7 +867,7 @@ public class StateMachineTest extends TestCase { // Set the initial state will be child 1 setInitialState(mChildState1); - if (DBG) Log.d(TAG, "StateMachine4: ctor X"); + if (DBG) log("StateMachine4: ctor X"); } class ParentState extends State { @@ -892,7 +912,7 @@ public class StateMachineTest extends TestCase { public void testStateMachine4() throws Exception { StateMachine4 sm4 = new StateMachine4("sm4"); sm4.start(); - if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 E"); + if (sm4.isDbg()) tlog("testStateMachine4 E"); synchronized (sm4) { // Send two messages @@ -903,7 +923,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm4.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine4: exception while waiting " + e.getMessage()); + tloge("testStateMachine4: exception while waiting " + e.getMessage()); } } @@ -921,7 +941,7 @@ public class StateMachineTest extends TestCase { assertEquals(sm4.mParentState, lr.getState()); assertEquals(sm4.mChildState2, lr.getOriginalState()); - if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X"); + if (sm4.isDbg()) tlog("testStateMachine4 X"); } /** @@ -947,7 +967,7 @@ public class StateMachineTest extends TestCase { // Set the initial state will be the child setInitialState(mChildState1); - if (DBG) Log.d(TAG, "StateMachine5: ctor X"); + if (DBG) log("StateMachine5: ctor X"); } class ParentState1 extends State { @@ -1187,7 +1207,7 @@ public class StateMachineTest extends TestCase { public void testStateMachine5() throws Exception { StateMachine5 sm5 = new StateMachine5("sm5"); sm5.start(); - if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 E"); + if (sm5.isDbg()) tlog("testStateMachine5 E"); synchronized (sm5) { // Send 6 messages @@ -1202,7 +1222,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm5.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine5: exception while waiting " + e.getMessage()); + tloge("testStateMachine5: exception while waiting " + e.getMessage()); } } @@ -1255,7 +1275,7 @@ public class StateMachineTest extends TestCase { assertEquals(sm5.mParentState2, lr.getState()); assertEquals(sm5.mParentState2, lr.getOriginalState()); - if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X"); + if (sm5.isDbg()) tlog("testStateMachine5 X"); } /** @@ -1274,7 +1294,7 @@ public class StateMachineTest extends TestCase { // Set the initial state setInitialState(mS1); - if (DBG) Log.d(TAG, "StateMachine6: ctor X"); + if (DBG) log("StateMachine6: ctor X"); } class S1 extends State { @@ -1315,7 +1335,7 @@ public class StateMachineTest extends TestCase { StateMachine6 sm6 = new StateMachine6("sm6"); sm6.start(); - if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 E"); + if (sm6.isDbg()) tlog("testStateMachine6 E"); synchronized (sm6) { // Send a message @@ -1325,7 +1345,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm6.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine6: exception while waiting " + e.getMessage()); + tloge("testStateMachine6: exception while waiting " + e.getMessage()); } } @@ -1336,11 +1356,11 @@ public class StateMachineTest extends TestCase { */ long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1; long expectedDelay = DELAY_TIME - DELAY_FUDGE; - if (sm6.isDbg()) Log.d(TAG, "testStateMachine6: expect " + arrivalTimeDiff + if (sm6.isDbg()) tlog("testStateMachine6: expect " + arrivalTimeDiff + " >= " + expectedDelay); assertTrue(arrivalTimeDiff >= expectedDelay); - if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 X"); + if (sm6.isDbg()) tlog("testStateMachine6 X"); } /** @@ -1361,7 +1381,7 @@ public class StateMachineTest extends TestCase { // Set the initial state setInitialState(mS1); - if (DBG) Log.d(TAG, "StateMachine7: ctor X"); + if (DBG) log("StateMachine7: ctor X"); } class S1 extends State { @@ -1421,7 +1441,7 @@ public class StateMachineTest extends TestCase { StateMachine7 sm7 = new StateMachine7("sm7"); sm7.start(); - if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 E"); + if (sm7.isDbg()) tlog("testStateMachine7 E"); synchronized (sm7) { // Send a message @@ -1431,7 +1451,7 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm7.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachine7: exception while waiting " + e.getMessage()); + tloge("testStateMachine7: exception while waiting " + e.getMessage()); } } @@ -1442,11 +1462,11 @@ public class StateMachineTest extends TestCase { */ long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2; long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE; - if (sm7.isDbg()) Log.d(TAG, "testStateMachine7: expect " + arrivalTimeDiff + if (sm7.isDbg()) tlog("testStateMachine7: expect " + arrivalTimeDiff + " >= " + expectedDelay); assertTrue(arrivalTimeDiff >= expectedDelay); - if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 X"); + if (sm7.isDbg()) tlog("testStateMachine7 X"); } /** @@ -1494,9 +1514,9 @@ public class StateMachineTest extends TestCase { @SmallTest public void testStateMachineUnhandledMessage() throws Exception { - StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("sm"); + StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("smUnhandledMessage"); sm.start(); - if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage E"); + if (sm.isDbg()) tlog("testStateMachineUnhandledMessage E"); synchronized (sm) { // Send 2 messages @@ -1508,15 +1528,15 @@ public class StateMachineTest extends TestCase { // wait for the messages to be handled sm.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachineUnhandledMessage: exception while waiting " + tloge("testStateMachineUnhandledMessage: exception while waiting " + e.getMessage()); } } - assertEquals(sm.getLogRecCount(), 2); + assertEquals(2, sm.getLogRecSize()); assertEquals(2, sm.mUnhandledMessageCount); - if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X"); + if (sm.isDbg()) tlog("testStateMachineUnhandledMessage X"); } /** @@ -1569,7 +1589,7 @@ public class StateMachineTest extends TestCase { @MediumTest public void testStateMachineSharedThread() throws Exception { - if (DBG) Log.d(TAG, "testStateMachineSharedThread E"); + if (DBG) tlog("testStateMachineSharedThread E"); // Create and start the handler thread HandlerThread smThread = new HandlerThread("testStateMachineSharedThread"); @@ -1578,7 +1598,8 @@ public class StateMachineTest extends TestCase { // Create the state machines StateMachineSharedThread sms[] = new StateMachineSharedThread[10]; for (int i = 0; i < sms.length; i++) { - sms[i] = new StateMachineSharedThread("sm", smThread.getLooper(), sms.length); + sms[i] = new StateMachineSharedThread("smSharedThread", + smThread.getLooper(), sms.length); sms[i].start(); } @@ -1594,14 +1615,14 @@ public class StateMachineTest extends TestCase { try { waitObject.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testStateMachineSharedThread: exception while waiting " + tloge("testStateMachineSharedThread: exception while waiting " + e.getMessage()); } } for (StateMachineSharedThread sm : sms) { - assertEquals(sm.getLogRecCount(), 4); - for (int i = 0; i < sm.getLogRecCount(); i++) { + assertEquals(4, sm.getLogRecCount()); + for (int i = 0; i < sm.getLogRecSize(); i++) { LogRec lr = sm.getLogRec(i); assertEquals(i+1, lr.getWhat()); assertEquals(sm.mS1, lr.getState()); @@ -1609,12 +1630,168 @@ public class StateMachineTest extends TestCase { } } - if (DBG) Log.d(TAG, "testStateMachineSharedThread X"); + if (DBG) tlog("testStateMachineSharedThread X"); + } + + static class Hsm1 extends StateMachine { + private static final String HSM1_TAG = "hsm1"; + + public static final int CMD_1 = 1; + public static final int CMD_2 = 2; + public static final int CMD_3 = 3; + public static final int CMD_4 = 4; + public static final int CMD_5 = 5; + + public static Hsm1 makeHsm1() { + Log.d(HSM1_TAG, "makeHsm1 E"); + Hsm1 sm = new Hsm1(HSM1_TAG); + sm.start(); + Log.d(HSM1_TAG, "makeHsm1 X"); + return sm; + } + + Hsm1(String name) { + super(name); + log("ctor E"); + + // Add states, use indentation to show hierarchy + addState(mP1); + addState(mS1, mP1); + addState(mS2, mP1); + addState(mP2); + + // Set the initial state + setInitialState(mS1); + log("ctor X"); + } + + class P1 extends State { + @Override + public void enter() { + log("P1.enter"); + } + @Override + public void exit() { + log("P1.exit"); + } + @Override + public boolean processMessage(Message message) { + boolean retVal; + log("P1.processMessage what=" + message.what); + switch(message.what) { + case CMD_2: + // CMD_2 will arrive in mS2 before CMD_3 + sendMessage(CMD_3); + deferMessage(message); + transitionTo(mS2); + retVal = true; + break; + default: + // Any message we don't understand in this state invokes unhandledMessage + retVal = false; + break; + } + return retVal; + } + } + + class S1 extends State { + @Override + public void enter() { + log("S1.enter"); + } + @Override + public void exit() { + log("S1.exit"); + } + @Override + public boolean processMessage(Message message) { + log("S1.processMessage what=" + message.what); + if (message.what == CMD_1) { + // Transition to ourself to show that enter/exit is called + transitionTo(mS1); + return HANDLED; + } else { + // Let parent process all other messages + return NOT_HANDLED; + } + } + } + + class S2 extends State { + @Override + public void enter() { + log("S2.enter"); + } + @Override + public void exit() { + log("S2.exit"); + } + @Override + public boolean processMessage(Message message) { + boolean retVal; + log("S2.processMessage what=" + message.what); + switch(message.what) { + case(CMD_2): + sendMessage(CMD_4); + retVal = true; + break; + case(CMD_3): + deferMessage(message); + transitionTo(mP2); + retVal = true; + break; + default: + retVal = false; + break; + } + return retVal; + } + } + + class P2 extends State { + @Override + public void enter() { + log("P2.enter"); + sendMessage(CMD_5); + } + @Override + public void exit() { + log("P2.exit"); + } + @Override + public boolean processMessage(Message message) { + log("P2.processMessage what=" + message.what); + switch(message.what) { + case(CMD_3): + break; + case(CMD_4): + break; + case(CMD_5): + transitionToHaltingState(); + break; + } + return HANDLED; + } + } + + @Override + protected void onHalting() { + log("halting"); + synchronized (this) { + this.notifyAll(); + } + } + + P1 mP1 = new P1(); + S1 mS1 = new S1(); + S2 mS2 = new S2(); + P2 mP2 = new P2(); } @MediumTest public void testHsm1() throws Exception { - if (DBG) Log.d(TAG, "testHsm1 E"); + if (DBG) tlog("testHsm1 E"); Hsm1 sm = Hsm1.makeHsm1(); @@ -1627,11 +1804,14 @@ public class StateMachineTest extends TestCase { try { sm.wait(); } catch (InterruptedException e) { - Log.e(TAG, "testHsm1: exception while waiting " + e.getMessage()); + tloge("testHsm1: exception while waiting " + e.getMessage()); } } + dumpLogRecs(sm); + assertEquals(7, sm.getLogRecCount()); + LogRec lr = sm.getLogRec(0); assertEquals(Hsm1.CMD_1, lr.getWhat()); assertEquals(sm.mS1, lr.getState()); @@ -1667,162 +1847,14 @@ public class StateMachineTest extends TestCase { assertEquals(sm.mP2, lr.getState()); assertEquals(sm.mP2, lr.getOriginalState()); - if (DBG) Log.d(TAG, "testStateMachineSharedThread X"); - } -} - -class Hsm1 extends StateMachine { - private static final String TAG = "hsm1"; - - public static final int CMD_1 = 1; - public static final int CMD_2 = 2; - public static final int CMD_3 = 3; - public static final int CMD_4 = 4; - public static final int CMD_5 = 5; - - public static Hsm1 makeHsm1() { - Log.d(TAG, "makeHsm1 E"); - Hsm1 sm = new Hsm1("hsm1"); - sm.start(); - Log.d(TAG, "makeHsm1 X"); - return sm; - } - - Hsm1(String name) { - super(name); - Log.d(TAG, "ctor E"); - - // Add states, use indentation to show hierarchy - addState(mP1); - addState(mS1, mP1); - addState(mS2, mP1); - addState(mP2); - - // Set the initial state - setInitialState(mS1); - Log.d(TAG, "ctor X"); - } - - class P1 extends State { - @Override - public void enter() { - Log.d(TAG, "P1.enter"); - } - @Override - public void exit() { - Log.d(TAG, "P1.exit"); - } - @Override - public boolean processMessage(Message message) { - boolean retVal; - Log.d(TAG, "P1.processMessage what=" + message.what); - switch(message.what) { - case CMD_2: - // CMD_2 will arrive in mS2 before CMD_3 - sendMessage(CMD_3); - deferMessage(message); - transitionTo(mS2); - retVal = true; - break; - default: - // Any message we don't understand in this state invokes unhandledMessage - retVal = false; - break; - } - return retVal; - } + if (DBG) tlog("testStateMachineSharedThread X"); } - class S1 extends State { - @Override - public void enter() { - Log.d(TAG, "S1.enter"); - } - @Override - public void exit() { - Log.d(TAG, "S1.exit"); - } - @Override - public boolean processMessage(Message message) { - Log.d(TAG, "S1.processMessage what=" + message.what); - if (message.what == CMD_1) { - // Transition to ourself to show that enter/exit is called - transitionTo(mS1); - return HANDLED; - } else { - // Let parent process all other messages - return NOT_HANDLED; - } - } + private void tlog(String s) { + Log.d(TAG, s); } - class S2 extends State { - @Override - public void enter() { - Log.d(TAG, "S2.enter"); - } - @Override - public void exit() { - Log.d(TAG, "S2.exit"); - } - @Override - public boolean processMessage(Message message) { - boolean retVal; - Log.d(TAG, "S2.processMessage what=" + message.what); - switch(message.what) { - case(CMD_2): - sendMessage(CMD_4); - retVal = true; - break; - case(CMD_3): - deferMessage(message); - transitionTo(mP2); - retVal = true; - break; - default: - retVal = false; - break; - } - return retVal; - } - } - - class P2 extends State { - @Override - public void enter() { - Log.d(TAG, "P2.enter"); - sendMessage(CMD_5); - } - @Override - public void exit() { - Log.d(TAG, "P2.exit"); - } - @Override - public boolean processMessage(Message message) { - Log.d(TAG, "P2.processMessage what=" + message.what); - switch(message.what) { - case(CMD_3): - break; - case(CMD_4): - break; - case(CMD_5): - transitionToHaltingState(); - break; - } - return HANDLED; - } + private void tloge(String s) { + Log.e(TAG, s); } - - @Override - protected void onHalting() { - Log.d(TAG, "halting"); - synchronized (this) { - this.notifyAll(); - } - } - - P1 mP1 = new P1(); - S1 mS1 = new S1(); - S2 mS2 = new S2(); - P2 mP2 = new P2(); } diff --git a/core/tests/inputmethodtests/Android.mk b/core/tests/inputmethodtests/Android.mk new file mode 100644 index 0000000..4631e65 --- /dev/null +++ b/core/tests/inputmethodtests/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_DX_FLAGS := --core-library +LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_PACKAGE_NAME := FrameworksCoreInputMethodTests + +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/core/tests/inputmethodtests/AndroidManifest.xml b/core/tests/inputmethodtests/AndroidManifest.xml new file mode 100644 index 0000000..7f0b1aa --- /dev/null +++ b/core/tests/inputmethodtests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + android:installLocation="internalOnly" + package="com.android.frameworks.coretests.inputmethod" + android:sharedUserId="android.uid.system"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.frameworks.coretests.inputmethod" + android:label="Frameworks InputMethod Core Tests" /> + +</manifest> diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh new file mode 100755 index 0000000..5e123ec --- /dev/null +++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +while [[ $# -gt 0 ]]; do + case "$1" in + --rebuild ) echo Rebuild && rebuild=true;; + * ) com_opts+=($1);; + esac + shift +done + +if [[ -z $ANDROID_PRODUCT_OUT && $rebuilld == true ]]; then + echo You must lunch before running this test. + exit 0 +fi + +if [[ $rebuild == true ]]; then + make -j4 FrameworksCoreInputMethodTests + TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreInputMethodTests.apk + COMMAND="adb install -r $TESTAPP" + echo $COMMAND + $COMMAND +fi + +adb shell am instrument -w -e class android.os.InputMethodTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java new file mode 100644 index 0000000..0a2b50c --- /dev/null +++ b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import com.android.internal.inputmethod.InputMethodUtils; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; + +import java.util.ArrayList; +import java.util.List; + +public class InputMethodTest extends InstrumentationTestCase { + private static final boolean IS_AUX = true; + private static final boolean IS_DEFAULT = true; + private static final boolean IS_AUTO = true; + + @SmallTest + public void testDefaultEnabledImesWithDefaultVoiceIme() throws Exception { + final Context context = getInstrumentation().getTargetContext(); + final ArrayList<InputMethodInfo> imis = new ArrayList<InputMethodInfo>(); + imis.add(createDefaultAutoDummyVoiceIme()); + imis.add(createNonDefaultAutoDummyVoiceIme0()); + imis.add(createNonDefaultAutoDummyVoiceIme1()); + imis.add(createNonDefaultDummyVoiceIme2()); + imis.add(createDefaultDummyEnUSKeyboardIme()); + imis.add(createNonDefaultDummyJaJPKeyboardIme()); + final ArrayList<InputMethodInfo> enabledImis = InputMethodUtils.getDefaultEnabledImes( + context, true, imis); + assertEquals(2, enabledImis.size()); + for (int i = 0; i < enabledImis.size(); ++i) { + final InputMethodInfo imi = enabledImis.get(0); + // "DummyDefaultAutoVoiceIme" and "DummyDefaultEnKeyboardIme" + if (imi.getPackageName().equals("DummyDefaultAutoVoiceIme") + || imi.getPackageName().equals("DummyDefaultEnKeyboardIme")) { + continue; + } else { + fail("Invalid enabled subtype."); + } + } + } + + @SmallTest + public void testDefaultEnabledImesWithOutDefaultVoiceIme() throws Exception { + final Context context = getInstrumentation().getTargetContext(); + final ArrayList<InputMethodInfo> imis = new ArrayList<InputMethodInfo>(); + imis.add(createNonDefaultAutoDummyVoiceIme0()); + imis.add(createNonDefaultAutoDummyVoiceIme1()); + imis.add(createNonDefaultDummyVoiceIme2()); + imis.add(createDefaultDummyEnUSKeyboardIme()); + imis.add(createNonDefaultDummyJaJPKeyboardIme()); + final ArrayList<InputMethodInfo> enabledImis = InputMethodUtils.getDefaultEnabledImes( + context, true, imis); + assertEquals(3, enabledImis.size()); + for (int i = 0; i < enabledImis.size(); ++i) { + final InputMethodInfo imi = enabledImis.get(0); + // "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1" and + // "DummyDefaultEnKeyboardIme" + if (imi.getPackageName().equals("DummyNonDefaultAutoVoiceIme0") + || imi.getPackageName().equals("DummyNonDefaultAutoVoiceIme1") + || imi.getPackageName().equals("DummyDefaultEnKeyboardIme")) { + continue; + } else { + fail("Invalid enabled subtype."); + } + } + } + + private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name, + CharSequence label, boolean isAuxIme, boolean isDefault, + List<InputMethodSubtype> subtypes) { + final ResolveInfo ri = new ResolveInfo(); + final ServiceInfo si = new ServiceInfo(); + final ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = packageName; + ai.enabled = true; + ai.flags |= ApplicationInfo.FLAG_SYSTEM; + si.applicationInfo = ai; + si.enabled = true; + si.packageName = packageName; + si.name = name; + si.exported = true; + si.nonLocalizedLabel = label; + ri.serviceInfo = si; + return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault); + } + + private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode, + boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) { + return new InputMethodSubtype(0, 0, locale, mode, "", isAuxiliary, + overridesImplicitlyEnabledSubtype); + } + + private static InputMethodInfo createDefaultAutoDummyVoiceIme() { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO)); + subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO)); + return createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", "dummy.voice0", + "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes); + } + + private static InputMethodInfo createNonDefaultAutoDummyVoiceIme0() { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO)); + subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO)); + return createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", "dummy.voice1", + "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes); + } + + private static InputMethodInfo createNonDefaultAutoDummyVoiceIme1() { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO)); + subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO)); + return createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", "dummy.voice2", + "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes); + } + + private static InputMethodInfo createNonDefaultDummyVoiceIme2() { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO)); + return createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", "dummy.voice3", + "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes); + } + + private static InputMethodInfo createDefaultDummyEnUSKeyboardIme() { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + subtypes.add(createDummyInputMethodSubtype("en_US", "keyboard", !IS_AUX, !IS_AUTO)); + return createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", "dummy.keyboard0", + "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes); + } + + private static InputMethodInfo createNonDefaultDummyJaJPKeyboardIme() { + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + subtypes.add(createDummyInputMethodSubtype("ja_JP", "keyboard", !IS_AUX, !IS_AUTO)); + return createDummyInputMethodInfo("DummyNonDefaultJaJPKeyboardIme", "dummy.keyboard1", + "DummyKeyboard1", !IS_AUX, !IS_DEFAULT, subtypes); + } +} |