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