Home | History | Annotate | Download | only in plugins
      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 
     30     public boolean hasVersionInfo() {
     31         return !mVersions.isEmpty();
     32     }
     33 
     34     public int getDefaultVersion() {
     35         return mVersions.get(mDefault).mVersion;
     36     }
     37 
     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 
     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 
     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 
     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 
    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 
    114         public InvalidVersionException(String str, boolean tooNew) {
    115             super(str);
    116             mTooNew = tooNew;
    117         }
    118 
    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 
    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 
    134         public Version(int version, boolean required) {
    135             mVersion = version;
    136             mRequired = required;
    137         }
    138     }
    139 }
    140