1 /*
2  * Copyright 2016, 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 package com.android.managedprovisioning.common;
17 
18 import android.os.Parcel;
19 import android.os.Parcelable;
20 import android.os.PersistableBundle;
21 import androidx.annotation.NonNull;
22 import android.text.TextUtils;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Objects;
28 import java.util.Set;
29 
30 public abstract class PersistableBundlable implements Parcelable {
toPersistableBundle()31     public abstract @NonNull PersistableBundle toPersistableBundle();
32 
getPersistableBundleFromParcel(Parcel parcel)33     public static PersistableBundle getPersistableBundleFromParcel(Parcel parcel) {
34         return parcel.readParcelable(PersistableBundle.class.getClassLoader());
35     }
36 
37     @Override
equals(Object object)38     public boolean equals(Object object) {
39         return isPersistableBundlableEquals(this, object);
40     }
41 
42     @Override
hashCode()43     public int hashCode() {
44         // Concatenated sorted keys should be good enough as a hash
45         List<String> keys = new ArrayList(toPersistableBundle().keySet());
46         Collections.sort(keys);
47         return TextUtils.join(",", keys).hashCode();
48     }
49 
50     @Override
describeContents()51     public int describeContents() {
52         return 0;
53     }
54 
55     @Override
writeToParcel(Parcel dest, int flags)56     public void writeToParcel(Parcel dest, int flags) {
57         dest.writeParcelable(toPersistableBundle(), flags);
58     }
59 
isPersistableBundlableEquals(PersistableBundlable pb1, Object obj)60     private static boolean isPersistableBundlableEquals(PersistableBundlable pb1, Object obj) {
61         if (pb1 == obj) {
62             return true;
63         }
64         if (obj == null || pb1.getClass() != obj.getClass()) {
65             return false;
66         }
67 
68         // obj has to be PersistableBundlable as it has the same class
69         PersistableBundlable pb2 = (PersistableBundlable) obj;
70         return isPersistableBundleEquals(pb1.toPersistableBundle(), pb2.toPersistableBundle());
71     }
72 
73     /**
74      * Compares two {@link PersistableBundle} objects are equals.
75      */
isPersistableBundleEquals(PersistableBundle obj1, PersistableBundle obj2)76     private static boolean isPersistableBundleEquals(PersistableBundle obj1, PersistableBundle obj2) {
77         if (obj1 == obj2) {
78             return true;
79         }
80         if (obj1 == null || obj2 == null || obj1.size() != obj2.size()) {
81             return false;
82         }
83         Set<String> keys = obj1.keySet();
84         for (String key : keys) {
85             Object val1 = obj1.get(key);
86             Object val2 = obj2.get(key);
87             if (!isPersistableBundleSupportedValueEquals(val1, val2)) {
88                 return false;
89             }
90         }
91         return true;
92     }
93 
94     /**
95      * Compares two values which type is supported by {@link PersistableBundle}.
96      *
97      * <p>If the type isn't supported. The equality is done by {@link Object#equals(Object)}.
98      */
isPersistableBundleSupportedValueEquals(Object val1, Object val2)99     private static boolean isPersistableBundleSupportedValueEquals(Object val1, Object val2) {
100         if (val1 == val2) {
101             return true;
102         } else if (val1 == null || val2 == null || !val1.getClass().equals(val2.getClass())) {
103             return false;
104         } else if (val1 instanceof PersistableBundle) {
105             return isPersistableBundleEquals((PersistableBundle) val1, (PersistableBundle) val2);
106         } else if (val1 instanceof int[]) {
107             return Arrays.equals((int[]) val1, (int[]) val2);
108         } else if (val1 instanceof long[]) {
109             return Arrays.equals((long[]) val1, (long[]) val2);
110         } else if (val1 instanceof double[]) {
111             return Arrays.equals((double[]) val1, (double[]) val2);
112         } else if (val1 instanceof boolean[]) {
113             return Arrays.equals((boolean[]) val1, (boolean[]) val2);
114         } else if (val1 instanceof String[]) {
115             return Arrays.equals((String[]) val1, (String[]) val2);
116         } else {
117             return Objects.equals(val1, val2);
118         }
119     }
120 
121 }
122