diff options
6 files changed, 180 insertions, 11 deletions
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java new file mode 100644 index 0000000..4aed5c1 --- /dev/null +++ b/core/java/android/annotation/RequiresPermission.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015 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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated element requires (or may require) one or more permissions. + * <p/> + * Example of requiring a single permission: + * <pre>{@code + * @RequiresPermission(Manifest.permission.SET_WALLPAPER) + * public abstract void setWallpaper(Bitmap bitmap) throws IOException; + * + * @RequiresPermission(ACCESS_COARSE_LOCATION) + * public abstract Location getLastKnownLocation(String provider); + * }</pre> + * Example of requiring at least one permission from a set: + * <pre>{@code + * @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + * public abstract Location getLastKnownLocation(String provider); + * }</pre> + * Example of requiring multiple permissions: + * <pre>{@code + * @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + * public abstract Location getLastKnownLocation(String provider); + * }</pre> + * Example of requiring separate read and write permissions for a content provider: + * <pre>{@code + * @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS)) + * @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS)) + * public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks"); + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD}) +public @interface RequiresPermission { + /** + * The name of the permission that is required, if precisely one permission + * is required. If more than one permission is required, specify either + * {@link #allOf()} or {@link #anyOf()} instead. + * <p> + * If specified, {@link #anyOf()} and {@link #allOf()} must both be null. + */ + String value() default ""; + + /** + * Specifies a list of permission names that are all required. + * <p> + * If specified, {@link #anyOf()} and {@link #value()} must both be null. + */ + String[] allOf() default {}; + + /** + * Specifies a list of permission names where at least one is required + * <p> + * If specified, {@link #allOf()} and {@link #value()} must both be null. + */ + String[] anyOf() default {}; + + /** + * If true, the permission may not be required in all cases (e.g. it may only be + * enforced on certain platforms, or for certain call parameters, etc. + */ + boolean conditional() default false; + + /** + * Specifies that the given permission is required for read operations + */ + @Target(FIELD) + @interface Read { + RequiresPermission value(); + } + + /** + * Specifies that the given permission is required for write operations + */ + @Target(FIELD) + @interface Write { + RequiresPermission value(); + } +} diff --git a/docs/html/training/enterprise/app-restrictions.jd b/docs/html/training/enterprise/app-restrictions.jd index fc5dfcc..dd2c2c0 100644 --- a/docs/html/training/enterprise/app-restrictions.jd +++ b/docs/html/training/enterprise/app-restrictions.jd @@ -316,8 +316,18 @@ if (!appCanUseCellular) { {@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. Your app has to listen for this intent so you can change the app's behavior when the restriction settings - change. The following code shows how to dynamically register a broadcast - receiver for this intent: + change.</p> + +<p class="note"> + <strong>Note:</strong> The {@link + android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED + ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent is sent only to listeners + that are dynamically registered, <em>not</em> to listeners that are declared + in the app manifest. +</p> +<p> + The following code shows how to dynamically register a broadcast receiver for + this intent: </p> <pre>IntentFilter restrictionsFilter = diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 0385d1e..8d99a64 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -387,6 +387,7 @@ final class SettingsState { } catch (Throwable t) { Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t); destination.failWrite(out); + throw new IllegalStateException("Failed to write settings, restoring backup", t); } finally { IoUtils.closeQuietly(out); } @@ -408,10 +409,9 @@ final class SettingsState { parser.setInput(in, null); parseStateLocked(parser); - // Any error while parsing is fatal. - } catch (Throwable t) { + } catch (XmlPullParserException | IOException e) { throw new IllegalStateException("Failed parsing settings file: " - + mStatePersistFile , t); + + mStatePersistFile , e); } finally { IoUtils.closeQuietly(in); } diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ccbd0a6..6e59029 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -570,4 +570,7 @@ <!-- Padding to be used on the bottom of the fingerprint icon on Keyguard so it better aligns with the other icons. --> <dimen name="fingerprint_icon_additional_padding">12dp</dimen> + + <!-- Minimum margin of the notification panel on the side, when being positioned dynamically --> + <dimen name="notification_panel_min_side_margin">48dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 03e5746..7d61099 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -187,6 +187,9 @@ public class NotificationPanelView extends PanelView implements private boolean mExpansionIsFromHeadsUp; private int mBottomBarHeight; private boolean mExpandingFromHeadsUp; + private int mPositionMinSideMargin; + private int mLastOrientation = -1; + private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() { @Override public void run() { @@ -236,6 +239,7 @@ public class NotificationPanelView extends PanelView implements mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); mSecureCameraLaunchManager = new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea); + mLastOrientation = getResources().getConfiguration().orientation; // recompute internal state when qspanel height changes mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() { @@ -268,6 +272,8 @@ public class NotificationPanelView extends PanelView implements getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance); mQsFalsingThreshold = getResources().getDimensionPixelSize( R.dimen.qs_falsing_threshold); + mPositionMinSideMargin = getResources().getDimensionPixelSize( + R.dimen.notification_panel_min_side_margin); } public void updateResources() { @@ -692,6 +698,9 @@ public class NotificationPanelView extends PanelView implements if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) { return true; } + if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { + updateVerticalPanelPosition(event.getX()); + } super.onTouchEvent(event); return true; } @@ -740,7 +749,7 @@ public class NotificationPanelView extends PanelView implements } private boolean isInQsArea(float x, float y) { - return (x >= mScrollView.getLeft() && x <= mScrollView.getRight()) && + return (x >= mScrollView.getX() && x <= mScrollView.getX() + mScrollView.getWidth()) && (y <= mNotificationStackScroller.getBottomMostNotificationBottom() || y <= mQsContainer.getY() + mQsContainer.getHeight()); } @@ -939,7 +948,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusBar.setAlpha(1f); mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); } - + resetVerticalPanelPosition(); updateQsState(); } @@ -1376,7 +1385,7 @@ public class NotificationPanelView extends PanelView implements return false; } View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader; - boolean onHeader = x >= header.getLeft() && x <= header.getRight() + boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth() && y >= header.getTop() && y <= header.getBottom(); if (mQsExpanded) { return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y); @@ -1771,6 +1780,10 @@ public class NotificationPanelView extends PanelView implements protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mAfforanceHelper.onConfigurationChanged(); + if (newConfig.orientation != mLastOrientation) { + resetVerticalPanelPosition(); + } + mLastOrientation = newConfig.orientation; } @Override @@ -2185,4 +2198,42 @@ public class NotificationPanelView extends PanelView implements mExpandingFromHeadsUp = true; } } + + @Override + protected void onClosingFinished() { + super.onClosingFinished(); + resetVerticalPanelPosition(); + } + + /** + * Updates the vertical position of the panel so it is positioned closer to the touch + * responsible for opening the panel. + * + * @param x the x-coordinate the touch event + */ + private void updateVerticalPanelPosition(float x) { + if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) { + resetVerticalPanelPosition(); + return; + } + float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; + float rightMost = getWidth() - mPositionMinSideMargin + - mNotificationStackScroller.getWidth() / 2; + if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { + x = getWidth() / 2; + } + x = Math.min(rightMost, Math.max(leftMost, x)); + setVerticalPanelTranslation(x - + (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth()/2)); + } + + private void resetVerticalPanelPosition() { + setVerticalPanelTranslation(0f); + } + + private void setVerticalPanelTranslation(float translation) { + mNotificationStackScroller.setTranslationX(translation); + mScrollView.setTranslationX(translation); + mHeader.setTranslationX(translation); + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b3aa966..252c16a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4275,6 +4275,8 @@ final class Settings { Slog.wtf(PackageManagerService.TAG, "Failed to write settings, restoring backup", t); destination.failWrite(out); + throw new IllegalStateException("Failed to write runtime permissions," + + " restoring backup", t); } finally { IoUtils.closeQuietly(out); } @@ -4322,10 +4324,9 @@ final class Settings { parser.setInput(in, null); parseRuntimePermissionsLPr(parser, userId); - // Any error while parsing is fatal. - } catch (Throwable t) { + } catch (XmlPullParserException | IOException e) { throw new IllegalStateException("Failed parsing permissions file: " - + permissionsFile , t); + + permissionsFile , e); } finally { IoUtils.closeQuietly(in); } |
