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.plugins; 16 17 import com.android.systemui.plugins.annotations.Dependencies; 18 import com.android.systemui.plugins.annotations.DependsOn; 19 import com.android.systemui.plugins.annotations.ProvidesInterface; 20 import com.android.systemui.plugins.annotations.Requirements; 21 import com.android.systemui.plugins.annotations.Requires; 22 23 import android.util.ArrayMap; 24 25 public class VersionInfo { 26 27 private final ArrayMap<Class<?>, Version> mVersions = new ArrayMap<>(); 28 private Class<?> mDefault; 29 hasVersionInfo()30 public boolean hasVersionInfo() { 31 return !mVersions.isEmpty(); 32 } 33 getDefaultVersion()34 public int getDefaultVersion() { 35 return mVersions.get(mDefault).mVersion; 36 } 37 addClass(Class<?> cls)38 public VersionInfo addClass(Class<?> cls) { 39 if (mDefault == null) { 40 // The legacy default version is from the first class we add. 41 mDefault = cls; 42 } 43 addClass(cls, false); 44 return this; 45 } 46 addClass(Class<?> cls, boolean required)47 private void addClass(Class<?> cls, boolean required) { 48 if (mVersions.containsKey(cls)) return; 49 ProvidesInterface provider = cls.getDeclaredAnnotation(ProvidesInterface.class); 50 if (provider != null) { 51 mVersions.put(cls, new Version(provider.version(), true)); 52 } 53 Requires requires = cls.getDeclaredAnnotation(Requires.class); 54 if (requires != null) { 55 mVersions.put(requires.target(), new Version(requires.version(), required)); 56 } 57 Requirements requirements = cls.getDeclaredAnnotation(Requirements.class); 58 if (requirements != null) { 59 for (Requires r : requirements.value()) { 60 mVersions.put(r.target(), new Version(r.version(), required)); 61 } 62 } 63 DependsOn depends = cls.getDeclaredAnnotation(DependsOn.class); 64 if (depends != null) { 65 addClass(depends.target(), true); 66 } 67 Dependencies dependencies = cls.getDeclaredAnnotation(Dependencies.class); 68 if (dependencies != null) { 69 for (DependsOn d : dependencies.value()) { 70 addClass(d.target(), true); 71 } 72 } 73 } 74 checkVersion(VersionInfo plugin)75 public void checkVersion(VersionInfo plugin) throws InvalidVersionException { 76 ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions); 77 plugin.mVersions.forEach((aClass, version) -> { 78 Version v = versions.remove(aClass); 79 if (v == null) { 80 v = createVersion(aClass); 81 } 82 if (v == null) { 83 throw new InvalidVersionException(aClass.getSimpleName() 84 + " does not provide an interface", false); 85 } 86 if (v.mVersion != version.mVersion) { 87 throw new InvalidVersionException(aClass, v.mVersion < version.mVersion, v.mVersion, 88 version.mVersion); 89 } 90 }); 91 versions.forEach((aClass, version) -> { 92 if (version.mRequired) { 93 throw new InvalidVersionException("Missing required dependency " 94 + aClass.getSimpleName(), false); 95 } 96 }); 97 } 98 createVersion(Class<?> cls)99 private Version createVersion(Class<?> cls) { 100 ProvidesInterface provider = cls.getDeclaredAnnotation(ProvidesInterface.class); 101 if (provider != null) { 102 return new Version(provider.version(), false); 103 } 104 return null; 105 } 106 hasClass(Class<T> cls)107 public <T> boolean hasClass(Class<T> cls) { 108 return mVersions.containsKey(cls); 109 } 110 111 public static class InvalidVersionException extends RuntimeException { 112 private final boolean mTooNew; 113 InvalidVersionException(String str, boolean tooNew)114 public InvalidVersionException(String str, boolean tooNew) { 115 super(str); 116 mTooNew = tooNew; 117 } 118 InvalidVersionException(Class<?> cls, boolean tooNew, int expected, int actual)119 public InvalidVersionException(Class<?> cls, boolean tooNew, int expected, int actual) { 120 super(cls.getSimpleName() + " expected version " + expected + " but had " + actual); 121 mTooNew = tooNew; 122 } 123 isTooNew()124 public boolean isTooNew() { 125 return mTooNew; 126 } 127 } 128 129 private static class Version { 130 131 private final int mVersion; 132 private final boolean mRequired; 133 Version(int version, boolean required)134 public Version(int version, boolean required) { 135 mVersion = version; 136 mRequired = required; 137 } 138 } 139 } 140