diff options
21 files changed, 578 insertions, 75 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ff589f9164c0..49b254936295 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2095,6 +2095,7 @@ public class Notification implements Parcelable try { Constructor<? extends Style> constructor = styleClass.getConstructor(); + constructor.setAccessible(true); style = constructor.newInstance(); style.restoreFromExtras(extras); } catch (Throwable t) { diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 50e7d1c44718..3fdabee588e5 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -584,7 +584,7 @@ public class StorageManager { // Nickname always takes precedence when defined if (!TextUtils.isEmpty(vol.fsUuid)) { final VolumeRecord rec = findRecordByUuid(vol.fsUuid); - if (!TextUtils.isEmpty(rec.nickname)) { + if (rec != null && !TextUtils.isEmpty(rec.nickname)) { return rec.nickname; } } diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java index 918933b50188..3319e6431128 100644 --- a/core/java/android/preference/GenericInflater.java +++ b/core/java/android/preference/GenericInflater.java @@ -376,6 +376,7 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> { Class clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name); constructor = clazz.getConstructor(mConstructorSignature); + constructor.setAccessible(true); sConstructorMap.put(name, constructor); } diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index ca37d495b366..cbf76bc3dc4b 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -211,10 +211,10 @@ public class TransitionInflater { .asSubclass(expectedType); if (c != null) { constructor = c.getConstructor(sConstructorSignature); + constructor.setAccessible(true); sConstructors.put(className, constructor); } } - constructor.setAccessible(true); return constructor.newInstance(mContext, attrs); } } catch (InstantiationException e) { diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index cd68fd18c791..ed7fd8634b6b 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -19,12 +19,17 @@ package android.transition; import com.android.internal.R; import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.Animator.AnimatorPauseListener; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; + /** * This transition tracks changes to the visibility of target views in the * start and end scenes. Visibility is determined not just by the @@ -286,6 +291,12 @@ public abstract class Visibility extends Transition { return null; } } + final boolean isForcedVisibility = mForcedStartVisibility != -1 || + mForcedEndVisibility != -1; + if (isForcedVisibility) { + // Make sure that we reverse the effect of onDisappear's setTransitionAlpha(0) + endValues.view.setTransitionAlpha(1); + } return onAppear(sceneRoot, endValues.view, startValues, endValues); } @@ -418,9 +429,9 @@ public abstract class Visibility extends Transition { sceneRoot.getOverlay().remove(overlayView); } else { final View finalOverlayView = overlayView; - animator.addListener(new AnimatorListenerAdapter() { + addListener(new TransitionListenerAdapter() { @Override - public void onAnimationEnd(Animator animation) { + public void onTransitionEnd(Transition transition) { finalSceneRoot.getOverlay().remove(finalOverlayView); } }); @@ -438,40 +449,10 @@ public abstract class Visibility extends Transition { } Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues); if (animator != null) { - final View finalViewToKeep = viewToKeep; - animator.addListener(new AnimatorListenerAdapter() { - boolean mCanceled = false; - - @Override - public void onAnimationPause(Animator animation) { - if (!mCanceled && !isForcedVisibility) { - finalViewToKeep.setVisibility(finalVisibility); - } - } - - @Override - public void onAnimationResume(Animator animation) { - if (!mCanceled && !isForcedVisibility) { - finalViewToKeep.setVisibility(View.VISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!mCanceled) { - if (isForcedVisibility) { - finalViewToKeep.setTransitionAlpha(0); - } else { - finalViewToKeep.setVisibility(finalVisibility); - } - } - } - }); + DisappearListener disappearListener = new DisappearListener(viewToKeep, + finalVisibility, isForcedVisibility); + animator.addListener(disappearListener); + addListener(disappearListener); } else if (!isForcedVisibility) { viewToKeep.setVisibility(originalVisibility); } @@ -517,4 +498,68 @@ public abstract class Visibility extends Transition { TransitionValues endValues) { return null; } + + private static class DisappearListener + extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener { + private final boolean mIsForcedVisibility; + private final View mView; + private final int mFinalVisibility; + + boolean mCanceled = false; + + public DisappearListener(View view, int finalVisibility, boolean isForcedVisibility) { + this.mView = view; + this.mIsForcedVisibility = isForcedVisibility; + this.mFinalVisibility = finalVisibility; + } + + @Override + public void onAnimationPause(Animator animation) { + if (!mCanceled && !mIsForcedVisibility) { + mView.setVisibility(mFinalVisibility); + } + } + + @Override + public void onAnimationResume(Animator animation) { + if (!mCanceled && !mIsForcedVisibility) { + mView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + hideViewWhenNotCanceled(); + } + + @Override + public void onTransitionEnd(Transition transition) { + hideViewWhenNotCanceled(); + } + + private void hideViewWhenNotCanceled() { + if (!mCanceled) { + if (mIsForcedVisibility) { + mView.setTransitionAlpha(0); + } else { + mView.setVisibility(mFinalVisibility); + } + } + } + } } diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 1503728fbb0e..37312d064948 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -590,6 +590,7 @@ public abstract class LayoutInflater { } } constructor = clazz.getConstructor(mConstructorSignature); + constructor.setAccessible(true); sConstructorMap.put(name, constructor); } else { // If we have a filter, apply it to cached constructor @@ -615,7 +616,6 @@ public abstract class LayoutInflater { Object[] args = mConstructorArgs; args[1] = attrs; - constructor.setAccessible(true); final View view = constructor.newInstance(args); if (view instanceof ViewStub) { // Use the same context when inflating ViewStub later. diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java index b49a59edbf98..dc8cadf8abdb 100644 --- a/core/java/android/view/MenuInflater.java +++ b/core/java/android/view/MenuInflater.java @@ -544,6 +544,7 @@ public class MenuInflater { try { Class<?> clazz = mContext.getClassLoader().loadClass(className); Constructor<?> constructor = clazz.getConstructor(constructorSignature); + constructor.setAccessible(true); return (T) constructor.newInstance(arguments); } catch (Exception e) { Log.w(LOG_TAG, "Cannot instantiate class: " + className, e); diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index f67dcb3cd0c9..efc171c359aa 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -53,8 +53,11 @@ import java.util.Arrays; * attribute identifier. * <p> * A touch feedback drawable may contain multiple child layers, including a - * special mask layer that is not drawn to the screen. A single layer may be set - * as the mask by specifying its android:id value as {@link android.R.id#mask}. + * special mask layer that is not drawn to the screen. A single layer may be + * set as the mask from XML by specifying its {@code android:id} value as + * {@link android.R.id#mask}. At run time, a single layer may be set as the + * mask using {@code setId(..., android.R.id.mask)} or an existing mask layer + * may be replaced using {@code setDrawableByLayerId(android.R.id.mask, ...)}. * <pre> * <code><!-- A red ripple masked against an opaque rectangle. --/> * <ripple android:color="#ffff0000"> diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index d15fa3941766..7c04f401b177 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -135,7 +135,7 @@ void RenderProxy::setName(const char* name) { SETUP_TASK(setName); args->context = mContext; args->name = name; - post(task); + postAndWait(task); } CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) { diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 687622226380..b2c9cd081e2e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -72,6 +72,9 @@ <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] --> <string name="available_via_passpoint">Available via %1$s</string> + <!-- Summary for Connected wifi network without internet --> + <string name="wifi_connected_no_internet">Connected, no Internet</string> + <!-- Bluetooth settings. Message when a device is disconnected --> <string name="bluetooth_disconnected">Disconnected</string> <!-- Bluetooth settings. Message when disconnecting from a device --> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 2fde4f92c053..53e69e316c94 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -17,9 +17,13 @@ package com.android.settingslib.wifi; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; +import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; @@ -27,6 +31,8 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.text.TextUtils; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.util.LruCache; @@ -700,6 +706,25 @@ public class AccessPoint implements Comparable<AccessPoint> { } } + // Case when there is wifi connected without internet connectivity. + final ConnectivityManager cm = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (state == DetailedState.CONNECTED) { + IWifiManager wifiManager = IWifiManager.Stub.asInterface( + ServiceManager.getService(Context.WIFI_SERVICE)); + Network nw; + + try { + nw = wifiManager.getCurrentNetwork(); + } catch (RemoteException e) { + nw = null; + } + NetworkCapabilities nc = cm.getNetworkCapabilities(nw); + if (nc != null && !nc.hasCapability(nc.NET_CAPABILITY_VALIDATED)) { + return context.getString(R.string.wifi_connected_no_internet); + } + } + String[] formats = context.getResources().getStringArray((ssid == null) ? R.array.wifi_status : R.array.wifi_status_with_ssid); int index = state.ordinal(); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1a75b8ab9485..8d1d1248137a 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1141,7 +1141,13 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); if (nai != null) { synchronized (nai) { - return new NetworkCapabilities(nai.networkCapabilities); + NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities); + if (nai.lastValidated) { + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } else { + nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } + return nc; } } return null; diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java new file mode 100644 index 000000000000..8734d976db2e --- /dev/null +++ b/services/core/java/com/android/server/notification/CalendarTracker.java @@ -0,0 +1,289 @@ +/* + * 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 com.android.server.notification; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.net.Uri; +import android.provider.BaseColumns; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Instances; +import android.service.notification.ZenModeConfig.EventInfo; +import android.util.Log; + +import java.io.PrintWriter; +import java.util.Date; +import java.util.Objects; + +public class CalendarTracker { + private static final String TAG = "ConditionProviders.CT"; + private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG); + private static final boolean DEBUG_ATTENDEES = false; + + private static final int EVENT_CHECK_LOOKAHEAD = 24 * 60 * 60 * 1000; + + private static final String[] INSTANCE_PROJECTION = { + Instances.BEGIN, + Instances.END, + Instances.TITLE, + Instances.VISIBLE, + Instances.EVENT_ID, + Instances.OWNER_ACCOUNT, + Instances.CALENDAR_ID, + }; + + private static final String INSTANCE_ORDER_BY = Instances.BEGIN + " ASC"; + + private static final String[] ATTENDEE_PROJECTION = { + Attendees.EVENT_ID, + Attendees.ATTENDEE_EMAIL, + Attendees.ATTENDEE_STATUS, + Attendees.ATTENDEE_TYPE, + }; + + private static final String ATTENDEE_SELECTION = Attendees.EVENT_ID + " = ? AND " + + Attendees.ATTENDEE_EMAIL + " = ?"; + + private final Context mContext; + + private Callback mCallback; + private boolean mRegistered; + + public CalendarTracker(Context context) { + mContext = context; + } + + public void setCallback(Callback callback) { + if (mCallback == callback) return; + mCallback = callback; + setRegistered(mCallback != null); + } + + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("mCallback="); pw.println(mCallback); + pw.print(prefix); pw.print("mRegistered="); pw.println(mRegistered); + } + + public void dumpContent(Uri uri) { + Log.d(TAG, "dumpContent: " + uri); + final Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null); + try { + int r = 0; + while (cursor.moveToNext()) { + Log.d(TAG, "Row " + (++r) + ": id=" + + cursor.getInt(cursor.getColumnIndex(BaseColumns._ID))); + for (int i = 0; i < cursor.getColumnCount(); i++) { + final String name = cursor.getColumnName(i); + final int type = cursor.getType(i); + Object o = null; + String typeName = null; + switch (type) { + case Cursor.FIELD_TYPE_INTEGER: + o = cursor.getLong(i); + typeName = "INTEGER"; + break; + case Cursor.FIELD_TYPE_STRING: + o = cursor.getString(i); + typeName = "STRING"; + break; + case Cursor.FIELD_TYPE_NULL: + o = null; + typeName = "NULL"; + break; + default: + throw new UnsupportedOperationException("type: " + type); + } + if (name.equals(BaseColumns._ID) + || name.toLowerCase().contains("sync") + || o == null) { + continue; + } + Log.d(TAG, " " + name + "(" + typeName + ")=" + o); + } + } + Log.d(TAG, " " + uri + " " + r + " rows"); + } finally { + cursor.close(); + } + } + + + + public CheckEventResult checkEvent(EventInfo filter, long time) { + final Uri.Builder uriBuilder = Instances.CONTENT_URI.buildUpon(); + ContentUris.appendId(uriBuilder, time); + ContentUris.appendId(uriBuilder, time + EVENT_CHECK_LOOKAHEAD); + final Uri uri = uriBuilder.build(); + final Cursor cursor = mContext.getContentResolver().query(uri, INSTANCE_PROJECTION, null, + null, INSTANCE_ORDER_BY); + final CheckEventResult result = new CheckEventResult(); + result.recheckAt = time + EVENT_CHECK_LOOKAHEAD; + try { + while (cursor.moveToNext()) { + final long begin = cursor.getLong(0); + final long end = cursor.getLong(1); + final String title = cursor.getString(2); + final boolean visible = cursor.getInt(3) == 1; + final int eventId = cursor.getInt(4); + final String owner = cursor.getString(5); + final long calendarId = cursor.getLong(6); + if (DEBUG) Log.d(TAG, String.format("%s %s-%s v=%s eid=%s o=%s cid=%s", title, + new Date(begin), new Date(end), visible, eventId, owner, calendarId)); + final boolean meetsTime = time >= begin && time < end; + final boolean meetsCalendar = visible + && (filter.calendar == 0 || filter.calendar == calendarId); + if (meetsCalendar) { + if (DEBUG) Log.d(TAG, " MEETS CALENDAR"); + final boolean meetsAttendee = meetsAttendee(filter, eventId, owner); + if (meetsAttendee) { + if (DEBUG) Log.d(TAG, " MEETS ATTENDEE"); + if (meetsTime) { + if (DEBUG) Log.d(TAG, " MEETS TIME"); + result.inEvent = true; + } + if (begin > time && begin < result.recheckAt) { + result.recheckAt = begin; + } else if (end > time && end < result.recheckAt) { + result.recheckAt = end; + } + } + } + } + } finally { + cursor.close(); + } + return result; + } + + private boolean meetsAttendee(EventInfo filter, int eventId, String email) { + String selection = ATTENDEE_SELECTION; + String[] selectionArgs = { Integer.toString(eventId), email }; + if (DEBUG_ATTENDEES) { + selection = null; + selectionArgs = null; + } + final Cursor cursor = mContext.getContentResolver().query(Attendees.CONTENT_URI, + ATTENDEE_PROJECTION, selection, selectionArgs, null); + try { + if (cursor.getCount() == 0) { + if (DEBUG) Log.d(TAG, "No attendees found"); + return true; + } + boolean rt = false; + while (cursor.moveToNext()) { + final long rowEventId = cursor.getLong(0); + final String rowEmail = cursor.getString(1); + final int status = cursor.getInt(2); + final int type = cursor.getInt(3); + final boolean meetsReply = meetsReply(filter.reply, status); + final boolean meetsAttendance = meetsAttendance(filter.attendance, type); + if (DEBUG) Log.d(TAG, (DEBUG_ATTENDEES ? String.format( + "rowEventId=%s, rowEmail=%s, ", rowEventId, rowEmail) : "") + + String.format("status=%s, type=%s, meetsReply=%s, meetsAttendance=%s", + attendeeStatusToString(status), attendeeTypeToString(type), meetsReply, + meetsAttendance)); + final boolean eventMeets = rowEventId == eventId && Objects.equals(rowEmail, email) + && meetsReply && meetsAttendance; + rt |= eventMeets; + } + return rt; + } finally { + cursor.close(); + } + } + + private void setRegistered(boolean registered) { + if (mRegistered == registered) return; + final ContentResolver cr = mContext.getContentResolver(); + if (mRegistered) { + cr.unregisterContentObserver(mObserver); + } + mRegistered = registered; + if (mRegistered) { + cr.registerContentObserver(Instances.CONTENT_URI, false, mObserver); + } + } + + private static String attendeeStatusToString(int status) { + switch (status) { + case Attendees.ATTENDEE_STATUS_NONE: return "ATTENDEE_STATUS_NONE"; + case Attendees.ATTENDEE_STATUS_ACCEPTED: return "ATTENDEE_STATUS_ACCEPTED"; + case Attendees.ATTENDEE_STATUS_DECLINED: return "ATTENDEE_STATUS_DECLINED"; + case Attendees.ATTENDEE_STATUS_INVITED: return "ATTENDEE_STATUS_INVITED"; + case Attendees.ATTENDEE_STATUS_TENTATIVE: return "ATTENDEE_STATUS_TENTATIVE"; + default: return "ATTENDEE_STATUS_UNKNOWN_" + status; + } + } + + private static String attendeeTypeToString(int type) { + switch (type) { + case Attendees.TYPE_NONE: return "TYPE_NONE"; + case Attendees.TYPE_REQUIRED: return "TYPE_REQUIRED"; + case Attendees.TYPE_OPTIONAL: return "TYPE_OPTIONAL"; + case Attendees.TYPE_RESOURCE: return "TYPE_RESOURCE"; + default: return "TYPE_" + type; + } + } + + private static boolean meetsAttendance(int attendance, int attendeeType) { + switch (attendance) { + case EventInfo.ATTENDANCE_OPTIONAL: + return attendeeType == Attendees.TYPE_OPTIONAL; + case EventInfo.ATTENDANCE_REQUIRED: + return attendeeType == Attendees.TYPE_REQUIRED; + default: // EventInfo.ATTENDANCE_REQUIRED_OR_OPTIONAL + return true; + } + } + + private static boolean meetsReply(int reply, int attendeeStatus) { + switch (reply) { + case EventInfo.REPLY_YES: + return attendeeStatus == Attendees.ATTENDEE_STATUS_ACCEPTED; + case EventInfo.REPLY_ANY_EXCEPT_NO: + return attendeeStatus != Attendees.ATTENDEE_STATUS_DECLINED; + default: // EventInfo.REPLY_ANY + return true; + } + } + + private final ContentObserver mObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange, Uri u) { + if (DEBUG) Log.d(TAG, "onChange selfChange=" + selfChange + " uri=" + u); + mCallback.onChanged(); + } + + @Override + public void onChange(boolean selfChange) { + if (DEBUG) Log.d(TAG, "onChange selfChange=" + selfChange); + } + }; + + public static class CheckEventResult { + public boolean inEvent; + public long recheckAt; + } + + public interface Callback { + void onChanged(); + } + +} diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index b36fcd2cc09c..8c6afafd8ec8 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -121,6 +121,9 @@ public class ConditionProviders extends ManagedServices { @Override public void onBootPhaseAppsCanStart() { super.onBootPhaseAppsCanStart(); + for (int i = 0; i < mSystemConditionProviders.size(); i++) { + mSystemConditionProviders.valueAt(i).onBootComplete(); + } if (mCallback != null) { mCallback.onBootComplete(); } diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java index b5b97d6bcfb6..07903e97a2d0 100644 --- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java +++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java @@ -34,12 +34,11 @@ import android.util.Slog; import com.android.server.notification.NotificationManagerService.DumpFilter; import java.io.PrintWriter; -import java.util.Date; /** Built-in zen condition provider for simple time-based conditions */ public class CountdownConditionProvider extends SystemConditionProviderService { - private static final String TAG = "ConditionProviders"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String TAG = "ConditionProviders.CCP"; + private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG); public static final ComponentName COMPONENT = new ComponentName("android", CountdownConditionProvider.class.getName()); @@ -74,6 +73,11 @@ public class CountdownConditionProvider extends SystemConditionProviderService { } @Override + public void onBootComplete() { + // noop + } + + @Override public IConditionProvider asInterface() { return (IConditionProvider) onBind(null); } @@ -170,8 +174,4 @@ public class CountdownConditionProvider extends SystemConditionProviderService { ts(time), time - now, span, ts(now)); } - private static String ts(long time) { - return new Date(time) + " (" + time + ")"; - } - } diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index 425e22200d17..dea63255594d 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -16,16 +16,23 @@ package com.android.server.notification; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.Uri; import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; +import android.service.notification.ZenModeConfig.EventInfo; import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.server.notification.CalendarTracker.CheckEventResult; import com.android.server.notification.NotificationManagerService.DumpFilter; import java.io.PrintWriter; @@ -34,20 +41,27 @@ import java.io.PrintWriter; * Built-in zen condition provider for calendar event-based conditions. */ public class EventConditionProvider extends SystemConditionProviderService { - private static final String TAG = "ConditionProviders"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String TAG = "ConditionProviders.ECP"; + private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG); public static final ComponentName COMPONENT = new ComponentName("android", EventConditionProvider.class.getName()); private static final String NOT_SHOWN = "..."; + private static final String SIMPLE_NAME = EventConditionProvider.class.getSimpleName(); + private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE"; + private static final int REQUEST_CODE_EVALUATE = 1; + private static final String EXTRA_TIME = "time"; + private final Context mContext = this; private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>(); + private final CalendarTracker mTracker = new CalendarTracker(mContext); private boolean mConnected; private boolean mRegistered; + private boolean mBootComplete; // don't hammer the calendar provider until boot completes. public EventConditionProvider() { - if (DEBUG) Slog.d(TAG, "new EventConditionProvider()"); + if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()"); } @Override @@ -62,14 +76,25 @@ public class EventConditionProvider extends SystemConditionProviderService { @Override public void dump(PrintWriter pw, DumpFilter filter) { - pw.println(" EventConditionProvider:"); + pw.print(" "); pw.print(SIMPLE_NAME); pw.println(":"); pw.print(" mConnected="); pw.println(mConnected); pw.print(" mRegistered="); pw.println(mRegistered); + pw.print(" mBootComplete="); pw.println(mBootComplete); pw.println(" mSubscriptions="); for (Uri conditionId : mSubscriptions) { pw.print(" "); pw.println(conditionId); } + pw.println(" mTracker="); + mTracker.dump(" ", pw); + } + + @Override + public void onBootComplete() { + if (DEBUG) Slog.d(TAG, "onBootComplete"); + if (mBootComplete) return; + mBootComplete = true; + evaluateSubscriptions(); } @Override @@ -98,8 +123,9 @@ public class EventConditionProvider extends SystemConditionProviderService { notifyCondition(conditionId, Condition.STATE_FALSE, "badCondition"); return; } - mSubscriptions.add(conditionId); - evaluateSubscriptions(); + if (mSubscriptions.add(conditionId)) { + evaluateSubscriptions(); + } } @Override @@ -121,9 +147,52 @@ public class EventConditionProvider extends SystemConditionProviderService { } private void evaluateSubscriptions() { + if (DEBUG) Log.d(TAG, "evaluateSubscriptions"); + if (!mBootComplete) { + if (DEBUG) Log.d(TAG, "Skipping evaluate before boot complete"); + return; + } + final long now = System.currentTimeMillis(); + mTracker.setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback); + setRegistered(!mSubscriptions.isEmpty()); + long reevaluateAt = 0; for (Uri conditionId : mSubscriptions) { - notifyCondition(conditionId, Condition.STATE_FALSE, "notImplemented"); + final EventInfo event = ZenModeConfig.tryParseEventConditionId(conditionId); + if (event == null) { + notifyCondition(conditionId, Condition.STATE_FALSE, "badConditionId"); + continue; + } + final CheckEventResult result = mTracker.checkEvent(event, now); + if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) { + reevaluateAt = result.recheckAt; + } + if (!result.inEvent) { + notifyCondition(conditionId, Condition.STATE_FALSE, "!inEventNow"); + continue; + } + notifyCondition(conditionId, Condition.STATE_TRUE, "inEventNow"); } + updateAlarm(now, reevaluateAt); + if (DEBUG) Log.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now)); + } + + private void updateAlarm(long now, long time) { + final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, + REQUEST_CODE_EVALUATE, + new Intent(ACTION_EVALUATE) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) + .putExtra(EXTRA_TIME, time), + PendingIntent.FLAG_UPDATE_CURRENT); + alarms.cancel(pendingIntent); + if (time == 0 || time < now) { + if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified" + : "specified time in the past")); + return; + } + if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s", + ts(time), formatDuration(time - now), ts(now))); + alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent); } private void notifyCondition(Uri conditionId, int state, String reason) { @@ -139,4 +208,34 @@ public class EventConditionProvider extends SystemConditionProviderService { return new Condition(id, summary, line1, line2, 0, state, Condition.FLAG_RELEVANT_ALWAYS); } + private void setRegistered(boolean registered) { + if (mRegistered == registered) return; + if (DEBUG) Slog.d(TAG, "setRegistered " + registered); + mRegistered = registered; + if (mRegistered) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(ACTION_EVALUATE); + registerReceiver(mReceiver, filter); + } else { + unregisterReceiver(mReceiver); + } + } + + private final CalendarTracker.Callback mTrackerCallback = new CalendarTracker.Callback() { + @Override + public void onChanged() { + if (DEBUG) Log.d(TAG, "mTrackerCallback.onChanged"); + evaluateSubscriptions(); + } + }; + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction()); + evaluateSubscriptions(); + } + }; } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 0912e9747d1a..bca13c202867 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -31,25 +31,24 @@ import android.service.notification.ZenModeConfig.ScheduleInfo; import android.util.ArraySet; import android.util.Log; import android.util.Slog; -import android.util.TimeUtils; import com.android.server.notification.NotificationManagerService.DumpFilter; import java.io.PrintWriter; -import java.util.Date; import java.util.TimeZone; /** * Built-in zen condition provider for daily scheduled time-based conditions. */ public class ScheduleConditionProvider extends SystemConditionProviderService { - private static final String TAG = "ConditionProviders"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String TAG = "ConditionProviders.SCP"; + private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG); public static final ComponentName COMPONENT = new ComponentName("android", ScheduleConditionProvider.class.getName()); private static final String NOT_SHOWN = "..."; - private static final String ACTION_EVALUATE = TAG + ".EVALUATE"; + private static final String SIMPLE_NAME = ScheduleConditionProvider.class.getSimpleName(); + private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE"; private static final int REQUEST_CODE_EVALUATE = 1; private static final String EXTRA_TIME = "time"; @@ -60,7 +59,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { private boolean mRegistered; public ScheduleConditionProvider() { - if (DEBUG) Slog.d(TAG, "new ScheduleConditionProvider()"); + if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()"); } @Override @@ -75,7 +74,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { @Override public void dump(PrintWriter pw, DumpFilter filter) { - pw.println(" ScheduleConditionProvider:"); + pw.print(" "); pw.print(SIMPLE_NAME); pw.println(":"); pw.print(" mConnected="); pw.println(mConnected); pw.print(" mRegistered="); pw.println(mRegistered); pw.println(" mSubscriptions="); @@ -94,6 +93,11 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } @Override + public void onBootComplete() { + // noop + } + + @Override public void onDestroy() { super.onDestroy(); if (DEBUG) Slog.d(TAG, "onDestroy"); @@ -175,16 +179,6 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } } - private static String ts(long time) { - return new Date(time) + " (" + time + ")"; - } - - private static String formatDuration(long millis) { - final StringBuilder sb = new StringBuilder(); - TimeUtils.formatDuration(millis, sb); - return sb.toString(); - } - private static boolean meetsSchedule(Uri conditionId, long time) { final ScheduleCalendar cal = toScheduleCalendar(conditionId); return cal != null && cal.isInSchedule(time); diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java index 3282a69af15c..8a8e0635e64d 100644 --- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java +++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java @@ -21,10 +21,12 @@ import android.content.Context; import android.net.Uri; import android.service.notification.ConditionProviderService; import android.service.notification.IConditionProvider; +import android.util.TimeUtils; import com.android.server.notification.NotificationManagerService.DumpFilter; import java.io.PrintWriter; +import java.util.Date; public abstract class SystemConditionProviderService extends ConditionProviderService { @@ -33,4 +35,15 @@ public abstract class SystemConditionProviderService extends ConditionProviderSe abstract public IConditionProvider asInterface(); abstract public ComponentName getComponent(); abstract public boolean isValidConditionId(Uri id); + abstract public void onBootComplete(); + + protected static String ts(long time) { + return new Date(time) + " (" + time + ")"; + } + + protected static String formatDuration(long millis) { + final StringBuilder sb = new StringBuilder(); + TimeUtils.formatDuration(millis, sb); + return sb.toString(); + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 66dd8f160b94..0feded35a742 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9149,7 +9149,11 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public boolean setDefaultBrowserPackageName(String packageName, int userId) { synchronized (mPackages) { - return mSettings.setDefaultBrowserPackageNameLPr(packageName, userId); + boolean result = mSettings.setDefaultBrowserPackageNameLPr(packageName, userId); + result |= updateIntentVerificationStatus(packageName, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, + UserHandle.myUserId()); + return result; } } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 0e888e8b3c3b..00cdc7150d67 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -25,6 +25,7 @@ import android.net.wifi.WifiChannel; import android.net.wifi.ScanResult; import android.net.wifi.WifiConnectionStatistics; import android.net.wifi.WifiActivityEnergyInfo; +import android.net.Network; import android.net.DhcpInfo; @@ -168,5 +169,7 @@ interface IWifiManager void disableEphemeralNetwork(String SSID); void factoryReset(); + + Network getCurrentNetwork(); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 320535158449..5e996725007f 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2804,4 +2804,17 @@ public class WifiManager { } catch (RemoteException e) { } } + + /** + * Get Network object of current wifi network + * @return Get Network object of current wifi network + * @hide + */ + public Network getCurrentNetwork() { + try { + return mService.getCurrentNetwork(); + } catch (RemoteException e) { + return null; + } + } } |