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