1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.shared.plugins;
16 
17 import android.util.ArrayMap;
18 
19 import com.android.systemui.plugins.annotations.Dependencies;
20 import com.android.systemui.plugins.annotations.DependsOn;
21 import com.android.systemui.plugins.annotations.ProvidesInterface;
22 import com.android.systemui.plugins.annotations.Requirements;
23 import com.android.systemui.plugins.annotations.Requires;
24 
25 import java.util.function.BiConsumer;
26 
27 public class VersionInfo {
28 
29     private final ArrayMap<Class<?>, Version> mVersions = new ArrayMap<>();
30     private Class<?> mDefault;
31 
hasVersionInfo()32     public boolean hasVersionInfo() {
33         return !mVersions.isEmpty();
34     }
35 
getDefaultVersion()36     public int getDefaultVersion() {
37         return mVersions.get(mDefault).mVersion;
38     }
39 
addClass(Class<?> cls)40     public VersionInfo addClass(Class<?> cls) {
41         if (mDefault == null) {
42             // The legacy default version is from the first class we add.
43             mDefault = cls;
44         }
45         addClass(cls, false);
46         return this;
47     }
48 
addClass(Class<?> cls, boolean required)49     private void addClass(Class<?> cls, boolean required) {
50         if (mVersions.containsKey(cls)) return;
51         ProvidesInterface provider = cls.getDeclaredAnnotation(ProvidesInterface.class);
52         if (provider != null) {
53             mVersions.put(cls, new Version(provider.version(), true));
54         }
55         Requires requires = cls.getDeclaredAnnotation(Requires.class);
56         if (requires != null) {
57             mVersions.put(requires.target(), new Version(requires.version(), required));
58         }
59         Requirements requirements = cls.getDeclaredAnnotation(Requirements.class);
60         if (requirements != null) {
61             for (Requires r : requirements.value()) {
62                 mVersions.put(r.target(), new Version(r.version(), required));
63             }
64         }
65         DependsOn depends = cls.getDeclaredAnnotation(DependsOn.class);
66         if (depends != null) {
67             addClass(depends.target(), true);
68         }
69         Dependencies dependencies = cls.getDeclaredAnnotation(Dependencies.class);
70         if (dependencies != null) {
71             for (DependsOn d : dependencies.value()) {
72                 addClass(d.target(), true);
73             }
74         }
75     }
76 
checkVersion(VersionInfo plugin)77     public void checkVersion(VersionInfo plugin) throws InvalidVersionException {
78         final ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
79         plugin.mVersions.forEach(new BiConsumer<Class<?>, Version>() {
80             @Override
81             public void accept(Class<?> aClass, Version version) {
82                 Version v = versions.remove(aClass);
83                 if (v == null) {
84                     v = VersionInfo.this.createVersion(aClass);
85                 }
86                 if (v == null) {
87                     throw new InvalidVersionException(aClass.getSimpleName()
88                             + " does not provide an interface", false);
89                 }
90                 if (v.mVersion != version.mVersion) {
91                     throw new InvalidVersionException(aClass, v.mVersion < version.mVersion,
92                             v.mVersion,
93                             version.mVersion);
94                 }
95             }
96         });
97         versions.forEach(new BiConsumer<Class<?>, Version>() {
98             @Override
99             public void accept(Class<?> aClass, Version version) {
100                 if (version.mRequired) {
101                     throw new InvalidVersionException("Missing required dependency "
102                             + aClass.getSimpleName(), false);
103                 }
104             }
105         });
106     }
107 
createVersion(Class<?> cls)108     private Version createVersion(Class<?> cls) {
109         ProvidesInterface provider = cls.getDeclaredAnnotation(ProvidesInterface.class);
110         if (provider != null) {
111             return new Version(provider.version(), false);
112         }
113         return null;
114     }
115 
hasClass(Class<T> cls)116     public <T> boolean hasClass(Class<T> cls) {
117         return mVersions.containsKey(cls);
118     }
119 
120     public static class InvalidVersionException extends RuntimeException {
121         private final boolean mTooNew;
122 
InvalidVersionException(String str, boolean tooNew)123         public InvalidVersionException(String str, boolean tooNew) {
124             super(str);
125             mTooNew = tooNew;
126         }
127 
InvalidVersionException(Class<?> cls, boolean tooNew, int expected, int actual)128         public InvalidVersionException(Class<?> cls, boolean tooNew, int expected, int actual) {
129             super(cls.getSimpleName() + " expected version " + expected + " but had " + actual);
130             mTooNew = tooNew;
131         }
132 
isTooNew()133         public boolean isTooNew() {
134             return mTooNew;
135         }
136     }
137 
138     private static class Version {
139 
140         private final int mVersion;
141         private final boolean mRequired;
142 
Version(int version, boolean required)143         public Version(int version, boolean required) {
144             mVersion = version;
145             mRequired = required;
146         }
147     }
148 }
149