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