• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.utils;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Build;
22 import android.util.Log;
23 
24 import java.lang.reflect.Field;
25 
26 /**
27  * Notify registered {@link Watcher}s when the content changes.
28  */
29 public interface Watchable {
30 
31     /**
32      * Ensures an observer is in the list, exactly once. The observer cannot be null.  The
33      * function quietly returns if the observer is already in the list.
34      *
35      * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes.
36      */
registerObserver(@onNull Watcher observer)37     public void registerObserver(@NonNull Watcher observer);
38 
39     /**
40      * Ensures an observer is not in the list. The observer must not be null.  The function
41      * quietly returns if the objserver is not in the list.
42      *
43      * @param observer The {@link Watcher} that should not be in the notification list.
44      */
unregisterObserver(@onNull Watcher observer)45     public void unregisterObserver(@NonNull Watcher observer);
46 
47     /**
48      * Return true if the {@link Watcher) is a registered observer.
49      * @param observer A {@link Watcher} that might be registered
50      * @return true if the observer is registered with this {@link Watchable}.
51      */
isRegisteredObserver(@onNull Watcher observer)52     public boolean isRegisteredObserver(@NonNull Watcher observer);
53 
54     /**
55      * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
56      * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
57      * is generally the first (deepest) {@link Watchable} to detect a change.
58      *
59      * @param what The {@link Watchable} that generated the event.
60      */
dispatchChange(@ullable Watchable what)61     public void dispatchChange(@Nullable Watchable what);
62 
63     /**
64      * Verify that all @Watched {@link Watchable} attributes are being watched by this
65      * class.  This requires reflection and only runs in engineering or user debug
66      * builds.
67      * @param base The object that contains watched attributes.
68      * @param observer The {@link Watcher} that should be watching these attributes.
69      * @param logOnly If true then log errors; if false then throw an RuntimeExecption on error.
70      */
verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly)71     static void verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly) {
72         if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
73             return;
74         }
75         for (Field f : base.getClass().getDeclaredFields()) {
76             final Watched annotation = f.getAnnotation(Watched.class);
77             if (annotation != null) {
78                 final String fn = base.getClass().getName() + "." + f.getName();
79                 try {
80                     f.setAccessible(true);
81                     final Object o = f.get(base);
82                     if (o instanceof Watchable) {
83                         Watchable attr = (Watchable) (o);
84                         if (attr != null && !attr.isRegisteredObserver(observer)) {
85                             handleVerifyError("Watchable " + fn + " missing an observer", logOnly);
86                         }
87                     } else if (!annotation.manual()) {
88                         handleVerifyError("@Watched annotated field " + fn + " is not a watchable"
89                                 + " type and is not flagged for manual watching.", logOnly);
90                     }
91                 } catch (IllegalAccessException e) {
92                     // The field is protected; ignore it.  Other exceptions that may be thrown by
93                     // Field.get() are allowed to roll up.
94                     handleVerifyError("Watchable " + fn + " not visible", logOnly);
95                 }
96             }
97         }
98     }
99 
handleVerifyError(String errorMessage, boolean logOnly)100     static void handleVerifyError(String errorMessage, boolean logOnly) {
101         if (logOnly) {
102             Log.e("Watchable", errorMessage);
103         } else {
104             throw new RuntimeException(errorMessage);
105         }
106     }
107 
108     /**
109      * Verify that all @Watched {@link Watchable} attributes are being watched by this
110      * class.  This calls verifyWatchedAttributes() with logOnly set to false.
111      * @param base The object that contains watched attributes.
112      * @param observer The {@link Watcher} that should be watching these attributes.
113      */
verifyWatchedAttributes(Object base, Watcher observer)114     static void verifyWatchedAttributes(Object base, Watcher observer) {
115         verifyWatchedAttributes(base, observer, false);
116     }
117 }
118